zoukankan      html  css  js  c++  java
  • POI2011题解

    POI2011题解

    2214先咕一会。。。

    [BZOJ2212][POI2011]Tree Rotations

    线段树合并模板题。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int gi(){
    	int x=0,w=1;char ch=getchar();
    	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    	if (ch=='-') w=0,ch=getchar();
    	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    const int N = 2e5+5;
    struct seg{int ls,rs,sz;}t[N*80];
    int n,tot;long long s1,s2,ans;
    int build(int l,int r,int p){
    	int x=++tot,mid=l+r>>1;t[x].sz=1;
    	if (l==r) return x;
    	if (p<=mid) t[x].ls=build(l,mid,p);
    	else t[x].rs=build(mid+1,r,p);
    	return x;
    }
    int merge(int x,int y){
    	if (!x||!y) return x|y;
    	int z=++tot;t[z].sz=t[x].sz+t[y].sz;
    	s1+=1ll*t[t[x].ls].sz*t[t[y].rs].sz;
    	s2+=1ll*t[t[x].rs].sz*t[t[y].ls].sz;
    	t[z].ls=merge(t[x].ls,t[y].ls);
    	t[z].rs=merge(t[x].rs,t[y].rs);
    	return z;
    }
    int dfs(){
    	int x=gi();if (x) return build(1,n,x);
    	int ls=dfs(),rs=dfs();s1=s2=0;
    	x=merge(ls,rs);ans+=min(s1,s2);return x;
    }
    int main(){n=gi();dfs();printf("%lld
    ",ans);return 0;}
    

    [BZOJ2213][Poi2011]Difference

    (s_{i,j})表示前(i)个位置里字符(j)的数量。我们相当于是要求(max{(s_{r,a}-s_{l,a})-(s_{r,b}-s_{l,b})}=max{(s_{r,a}-s_{r,b})-(s_{l,a}-s_{l,b})}),其中(s_{r,b}-s_{l,b} eq0)

    所以只需要维护前缀(s_{l,a}-s_{l,b})的最小值即可,每加一个字符至多只会使(26)个最小值发生改变,但因为(s_{r,b}-s_{l,b} eq0)的这个烦人的限制,所以还需要记录一下更新最小值的位置。复杂度(O(26n))

    最后还要(O(26^2))判一遍。给组数据:(s=)'aab'

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int n,sum[26],las[26],p[26][26],mn[26][26],ans;char s[1000005];
    int main(){
    	scanf("%d%s",&n,s+1);
    	for (int i=1;i<=n;++i){
    		int c=s[i]-'a';++sum[c];las[c]=i;
    		for (int j=0;j<26;++j)
    			if (sum[j])
    				ans=max(ans,sum[c]-sum[j]-mn[c][j]-(las[j]==p[c][j]));
    		for (int j=0;j<26;++j)
    			if (sum[j]-sum[c]<mn[j][c])
    				mn[j][c]=sum[j]-sum[c],p[j][c]=i;
    	}
    	for (int i=0;i<26;++i)
    		for (int j=0;j<26;++j)
    			if (sum[j])
    				ans=max(ans,sum[i]-sum[j]-mn[i][j]-(las[j]==p[i][j]));
    	printf("%d
    ",ans);return 0;
    }
    

    [BZOJ2215][Poi2011]Conspiracy

    先构造出一组合法方案。可以使用(mbox{2-sat}),对于任两个点,如果他们之间有边则不能同时存在于独立集中,否则不能同时存在与团中。这样的复杂度是(O(n^2))的。

    然后考虑从当前方案出发生成其余的方案。可以发现,一个集合中不可能有超过一个点进入另一个集合,所以新方案生成只有三种方法:从独立集中丢一个点进团,从团中丢一个点进独立集,或者是双方交换一对点。

    所以就维护一下每个点是不是只与对方集合中的某一个点冲突,然后统计答案即可。注意一下两个点集的数量。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int gi(){
    	int x=0,w=1;char ch=getchar();
    	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    	if (ch=='-') w=0,ch=getchar();
    	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    const int N = 5005;
    int n,a[N][N],to[N*N],nxt[N*N],head[N<<1],cnt,dfn[N<<1],low[N<<1],tim,vis[N<<1],S[N<<1],bel[N<<1],scc,col[N],ban[N],tot[2],ans;
    void link(int u,int v){
    	to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;
    	u^=1;v^=1;swap(u,v);
    	to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;
    }
    void Tarjan(int u){
    	dfn[u]=low[u]=++tim;vis[S[++S[0]]=u]=1;
    	for (int e=head[u];e;e=nxt[e])
    		if (!dfn[to[e]]) Tarjan(to[e]),low[u]=min(low[u],low[to[e]]);
    		else if (vis[to[e]]) low[u]=min(low[u],dfn[to[e]]);
    	if (dfn[u]==low[u]){
    		++scc;int x;
    		do x=S[S[0]--],vis[x]=0,bel[x]=scc;while (x^u);
    	}
    }
    int main(){
    	n=gi();
    	for (int i=1,j;i<=n;++i){
    		j=gi();while (j--) a[i][gi()]=1;
    	}
    	for (int i=1;i<=n;++i)
    		for (int j=i+1;j<=n;++j)
    			if (a[i][j]) link(i<<1|1,j<<1);
    			else link(i<<1,j<<1|1);
    	for (int i=2;i<=(n<<1|1);++i) if (!dfn[i]) Tarjan(i);
    	for (int i=1;i<=n;++i) if (bel[i<<1]==bel[i<<1|1]) return puts("0"),0;
    	for (int i=1;i<=n;++i) col[i]=bel[i<<1]<bel[i<<1|1],++tot[col[i]];
    	if (tot[0]&&tot[1]) ++ans;
    	for (int i=1;i<=n;++i)
    		for (int j=1;j<=n;++j)
    			if (col[i]!=col[j]&&a[i][j]!=col[j]){
    				if (!ban[i]) ban[i]=j;
    				else {ban[i]=-1;break;}
    			}
    	if (tot[0]>1)
    		for (int i=1;i<=n;++i)
    			if (col[i]==0&&!ban[i]) ++ans;
    	if (tot[1]>1)
    		for (int i=1;i<=n;++i)
    			if (col[i]==1&&!ban[i]) ++ans;
    	if (tot[0]&&tot[1])
    		for (int i=1;i<=n;++i)
    			for (int j=i+1;j<=n;++j)
    				if (col[i]!=col[j]&&(ban[i]==0||ban[i]==j)&&(ban[j]==0||ban[j]==i)) ++ans;
    	printf("%d
    ",ans);return 0;
    }
    

    [BZOJ2216][Poi2011]Lightning Conductor

    决策单调性模板题。

    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    int gi(){
    	int x=0,w=1;char ch=getchar();
    	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    	if (ch=='-') w=0,ch=getchar();
    	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    const int N = 5e5+5;
    int n,hd,tl;double a[N],dp1[N],dp2[N];
    struct node{int p,l,r;}q[N];
    double cal(int j,int i){
    	return a[j]+sqrt(i-j)-a[i];
    }
    int binary(int x,int y){
    	int l=q[tl].l,r=n,res=n;
    	while (l<=r){
    		int mid=l+r>>1;
    		if (cal(x,mid)>cal(y,mid)) res=mid,r=mid-1;
    		else l=mid+1;
    	}
    	return l;
    }
    void work(double *dp){
    	hd=1,tl=0;
    	for (int i=1;i<=n;++i){
    		++q[hd].l;
    		if (hd<=tl&&q[hd].l>q[hd].r) ++hd;
    		if (hd>tl||cal(i,n)>cal(q[tl].p,n)){
    			while (hd<=tl&&cal(i,q[tl].l)>cal(q[tl].p,q[tl].l)) --tl;
    			if (hd>tl) q[++tl]=(node){i,i,n};
    			else{
    				int x=binary(i,q[tl].p);
    				q[tl].r=x-1;q[++tl]=(node){i,x,n};
    			}
    		}
    		dp[i]=cal(q[hd].p,i);
    	}
    }
    int main(){
    	n=gi();
    	for (int i=1;i<=n;++i) a[i]=gi();
    	work(dp1);
    	reverse(a+1,a+n+1);
    	work(dp2);
    	for (int i=1;i<=n;++i) printf("%d
    ",(int)ceil(max(dp1[i],dp2[n-i+1])));
    	return 0;
    }
    

    [BZOJ2217][Poi2011]Lollipopz

    因为只有(1)或者(2),所以对这个序列求前缀和后,不能用前缀和表示的数一定不相邻。

    或者说,如果一个数(x)不能被某个前缀和表示出来,那么(x-1)(x+1)都一定可以被表示出来。(边界情况除外)

    如果询问的(x)可以被前缀和表示出来我们就直接用这个前缀,否则,找到(x+1)对应的前缀,把左右端点同时右移,直到左端点变成(1)或者右端点(+1)变成(1),就可以使这个区间和(-1)从而表示出(x)

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int gi(){
    	int x=0,w=1;char ch=getchar();
    	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    	if (ch=='-') w=0,ch=getchar();
    	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    const int N = 1e6+5;
    int n,m,sum[N],f[N<<1],len[N];char s[N];
    int main(){
    	n=gi();m=gi();scanf("%s",s+1);
    	for (int i=1;i<=n;++i) sum[i]=sum[i-1]+(s[i]=='W'?1:2),f[sum[i]]=i;
    	for (int i=n;i;--i) if (s[i]=='T') len[i]=len[i+1]+1;
    	while (m--){
    		int x=gi();
    		if (x>sum[n]) puts("NIE");
    		else if (f[x]) printf("1 %d
    ",f[x]);
    		else if (len[f[x+1]]>len[1]) printf("%d %d
    ",2+len[1],f[x+1]+len[1]);
    		else if (f[x+1]+len[f[x+1]]<=n) printf("%d %d
    ",1+len[f[x+1]],f[x+1]+len[f[x+1]]);
    		else puts("NIE");
    	}
    	return 0;
    }
    

    [BZOJ2276][Poi2011]Temperature

    之前萝卜考试蒯的原题。在考场上(YY)了一个神奇的链表维护(dp)的做法。

    大致就是说设(f_i)表示长度为(i)时最后一位的最小值。对于每个([L,R]),可以把前缀(le L)的全部改成(L),再把后面大于(R)的删掉就行了。复杂度(O(n))

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int gi(){
        int x=0,w=1;char ch=getchar();
        while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
        if (ch=='-') w=0,ch=getchar();
        while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
        return w?x:-x;
    }
    const int N = 1e6+5;
    int n,pre[N],nxt[N],val[N],sz[N],tot,hd,tl,len,ans;
    int main(){
        n=gi();
        for (int i=1;i<=n;++i){
            int L=gi(),R=gi(),S=1;
            while (hd&&val[hd]<=L) S+=sz[hd],hd=nxt[hd],pre[hd]=0;
            if (!hd) val[hd=tl=++tot]=L,sz[tot]=S;
            else pre[hd]=++tot,nxt[tot]=hd,val[tot]=L,sz[tot]=S,hd=tot;
            ++len;
            while (tl&&val[tl]>R) len-=sz[tl],tl=pre[tl],nxt[tl]=0;
            ans=max(ans,len);
        }
        printf("%d
    ",ans);return 0;
    }
    

    [BZOJ2277][Poi2011]Strongbox

    首先密码的构成一定是(0,x,2x,3x...),其中(x|n)。题中要求最大化密码的数量,也就是最小化(x)。已知(x|a_k,x mid a_i(1le i < k))。那么(gcd(a_i,a_k)(1le i<k))以及它们的因子都不能作为答案。把(gcd(n,a_k))的所有因子拿出来,根据其内部的拓扑关系递推一下即可。复杂度最低可以做到(包含的质因子个数O(d(n) imes nmbox{包含的质因子个数}))

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define ll long long
    ll gi(){
    	ll x=0,w=1;char ch=getchar();
    	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    	if (ch=='-') w=0,ch=getchar();
    	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    const int N = 250005;
    ll n,a[N],d[N],p[N];
    int k,tot,cnt,ban[N];
    int main(){
    	n=gi();k=gi();
    	for (int i=1;i<=k;++i) a[i]=gi();
    	a[k]=__gcd(a[k],n);
    	for (ll i=1;i*i<=a[k];++i)
    		if (a[k]%i==0){
    			d[++tot]=i;
    			if (i*i<a[k]) d[++tot]=a[k]/i;
    		}
    	sort(d+1,d+tot+1);ll tmp=a[k];
    	for (ll i=2;i*i<=tmp;++i)
    		if (tmp%i==0){
    			p[++cnt]=i;
    			while (tmp%i==0) tmp/=i;
    		}
    	if (tmp>1) p[++cnt]=tmp;
    	for (int i=1;i<k;++i)
    		ban[lower_bound(d+1,d+tot+1,__gcd(a[i],a[k]))-d]=1;
    	for (int i=tot;i;--i)
    		if (ban[i])
    			for (int j=1;j<=cnt;++j)
    				if (d[i]%p[j]==0)
    					ban[lower_bound(d+1,d+tot+1,d[i]/p[j])-d]=1;
    	for (int i=1;i<=tot;++i)
    		if (!ban[i]) return printf("%lld
    ",n/d[i]),0;
    }
    

    [BZOJ2278][Poi2011]Garbage

    一定存在一种方案使得所有的简单环不相交。所以那些不需要改变状态的边是没有用的。

    直接(dfs),搜到一个环就把它拿出来删掉。注意要加上当前弧优化不然要(TLE)

    #include<cstdio>
    #include<algorithm>
    #include<vector>
    using namespace std;
    int gi(){
    	int x=0,w=1;char ch=getchar();
    	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    	if (ch=='-') w=0,ch=getchar();
    	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    const int N = 1e5+5;
    const int M = 2e6+5;
    int n,m,to[M],nxt[M],head[N],cnt=1,du[N],S[N],ins[N],vis[M],tot;
    vector<int>ans[M];
    void dfs(int u){
    	if (ins[u]){
    		++tot;int x=0;
    		do x=S[S[0]--],ins[x]=0,ans[tot].push_back(x);while (x^u);
    	}
    	for (int &e=head[u];e;e=nxt[e])
    		if (!vis[e]){
    			vis[e]=vis[e^1]=1;
    			ins[S[++S[0]]=u]=1;
    			dfs(to[e]);
    		}
    }
    int main(){
    	n=gi();m=gi();
    	for (int i=1;i<=m;++i){
    		int u=gi(),v=gi();
    		if (gi()^gi()){
    			to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;
    			to[++cnt]=u;nxt[cnt]=head[v];head[v]=cnt;
    			++du[u];++du[v];
    		}
    	}
    	for (int i=1;i<=n;++i) if (du[i]&1) return puts("NIE"),0;
    	for (int i=1;i<=n;++i) dfs(i);
    	printf("%d
    ",tot);
    	for (int i=1;i<=tot;++i){
    		int sz=ans[i].size();printf("%d ",sz);
    		for (int j=0;j<sz;++j) printf("%d ",ans[i][j]);
    		printf("%d
    ",ans[i][0]);
    	}
    	return 0;
    }
    

    [BZOJ2280][Poi2011]Plot

    二分答案,然后做一个最小圆覆盖。该算法复杂度在期望意义下是(O(n)),证明略。

    做圆覆盖的时候需要再次二分最大的合法右端点,但是直接二分可能导致复杂度退化,可以先倍增求出最大的(k)使得长为(2^{k-1})的区间合法而长为(2^k)的区间不合法,再在([2^{k-1},2^k))里二分右端点。

    对着Claris代码调的,写得可能有点凌乱哈。

    #include<cstdio>
    #include<algorithm>
    #include<ctime>
    #include<cmath>
    using namespace std;
    
    const int N = 1e5+5;
    const double eps = 1e-10;
    
    struct node{double x,y;}a[N],b[N],O;
    int n,m,len,tim=50,tot,ans[2][N];double R;
    
    double dist(node a,node b){
    	return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
    }
    
    void work(node A,node B,node C){
    	double a,b,c,d,e,f;
    	a=A.x-C.x;b=A.y-C.y;
    	c=B.x-C.x;d=B.y-C.y;
    	e=A.x*A.x+A.y*A.y-C.x*C.x-C.y*C.y;
    	f=B.x*B.x+B.y*B.y-C.x*C.x-C.y*C.y;
    	O.x=(d*e-b*f)/(2*a*d-2*b*c);
    	O.y=(a*f-c*e)/(2*a*d-2*b*c);
    	R=dist(O,A);
    }
    
    void calc(int l,int r){
    	len=0;
    	for (int i=l;i<=r;++i) b[++len]=a[i];
    	for (int i=1;i<=len;++i) swap(b[i],b[1+rand()%len]);
    	O=b[1];R=0;
    	for (int i=1;i<=len;++i)
    		if (dist(O,b[i])>R+eps){
    			O=b[i];R=0;
    			for (int j=1;j<i;++j)
    				if (dist(O,b[j])>R+eps){
    					O.x=(b[i].x+b[j].x)/2;
    					O.y=(b[i].y+b[j].y)/2;
    					R=dist(b[i],O);
    					for (int k=1;k<j;++k)
    						if (dist(O,b[k])>R+eps)
    							work(b[i],b[j],b[k]);
    				}
    		}
    }
    bool check(double lim){
    	tot=0;
    	for (int i=1,j,t,l,r;i<=n;i=j+1){
    		for (t=1;i+(1<<t)-1<=n;++t){
    			calc(i,i+(1<<t)-1);
    			if (R>lim+eps) break;
    		}
    		j=i,l=i+(1<<t-1)-1,r=min(n,i+(1<<t)-1);
    		while (l<=r){
    			int mid=l+r>>1;calc(i,mid);
    			if (R<lim+eps) j=mid,l=mid+1;
    			else r=mid-1;
    		}
    		ans[0][++tot]=i;ans[1][tot]=j;
    		if (tot>m) return false;
    	}
    	return true;
    }
    
    int main(){
    	scanf("%d%d",&n,&m);srand(20020415);
    	for (int i=1;i<=n;++i) scanf("%lf%lf",&a[i].x,&a[i].y);
    	calc(1,n);double l=0,r=R;
    	if (m>1)
    		while (tim--&&r-l>eps){
    			double mid=(l+r)/2;
    			if (check(mid)) r=mid;else l=mid;
    		}
    	check(r);printf("%.8f
    %d
    ",r,tot);
    	for (int i=1;i<=tot;++i) calc(ans[0][i],ans[1][i]),printf("%.8f %.8f
    ",O.x,O.y);
    	return 0;
    }
    

    [BZOJ2525][Poi2011]Dynamite

    二分答案,然后贪心覆盖就可以了。对每个点维护(f_i)表示子树中最近的已覆盖点到(i)的距离,(g_i)表示子树中最远的未覆盖点到(i)的距离,转移讨论一下即可。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int gi(){
    	int x=0,w=1;char ch=getchar();
    	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    	if (ch=='-') w=0,ch=getchar();
    	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    const int N = 3e5+5;
    const int inf = 1<<30;
    int n,m,mrk[N],to[N<<1],nxt[N<<1],head[N],cnt,k,tot,f[N],g[N];
    void dfs(int u,int fa){
    	g[u]=mrk[u]?0:-inf;f[u]=inf;
    	for (int e=head[u];e;e=nxt[e])
    		if (to[e]!=fa){
    			int v=to[e];dfs(v,u);
    			if (g[u]+f[v]+1<=k) g[u]=-inf;
    			if (f[u]+g[v]+1>k) g[u]=max(g[u],g[v]+1);
    			f[u]=min(f[u],f[v]+1);
    		}
    	if (g[u]==k) f[u]=0,++tot;
    	if (f[u]+g[u]<=k) g[u]=-inf;
    }
    bool check(int mid){
    	k=mid;tot=0;dfs(1,0);
    	if (f[1]+g[1]>k) ++tot;
    	return tot<=m;
    }
    int main(){
    	n=gi();m=gi();
    	for (int i=1;i<=n;++i) mrk[i]=gi();
    	for (int i=1;i<n;++i){
    		int u=gi(),v=gi();
    		to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;
    		to[++cnt]=u;nxt[cnt]=head[v];head[v]=cnt;
    	}
    	int l=0,r=n-1,res=0;
    	while (l<=r){
    		int mid=l+r>>1;
    		if (check(mid)) res=mid,r=mid-1;
    		else l=mid+1;
    	}
    	printf("%d
    ",res);return 0;
    }
    

    [BZOJ2526][Poi2011]Inspection

    如果一个点要有答案那就需要满足最大子树的大小不超过(frac n2),而显然这性质只有重心才满足。所以实际上只有至多两个位置有答案其余的都是(-1)

    答案显然就是所有点到重心的距离减去最深的深度。注意如果有两个重心,那么当我们处理一个重心的时候,另一个重心所在的子树会比剩下的子树加起来还要大,这时候最深深度就必须在另一个重心所在的子树里面选。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int gi(){
    	int x=0,w=1;char ch=getchar();
    	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    	if (ch=='-') w=0,ch=getchar();
    	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    const int N = 1e6+5;
    int n,to[N<<1],nxt[N<<1],head[N],cnt,sz[N],w[N],f[N];
    long long ans;
    void dfs1(int u,int fa){
    	sz[u]=1;
    	for (int e=head[u];e;e=nxt[e])
    		if (to[e]!=fa){
    			dfs1(to[e],u),sz[u]+=sz[to[e]];
    			w[u]=max(w[u],sz[to[e]]);
    		}
    	w[u]=max(w[u],n-sz[u]);
    }
    void dfs2(int u,int fa){
    	sz[u]=1;f[u]=0;
    	for (int e=head[u];e;e=nxt[e])
    		if (to[e]!=fa){
    			dfs2(to[e],u),sz[u]+=sz[to[e]];
    			f[u]=max(f[u],f[to[e]]+1);
    			ans+=sz[to[e]];
    		}
    }
    int main(){
    	n=gi();
    	for (int i=1;i<n;++i){
    		int u=gi(),v=gi();
    		to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;
    		to[++cnt]=u;nxt[cnt]=head[v];head[v]=cnt;
    	}
    	dfs1(1,0);
    	for (int i=1;i<=n;++i)
    		if (w[i]<=(n>>1)){
    			ans=0;dfs2(i,0);ans<<=1;
    			if ((~n&1)&&w[i]==(n>>1)){
    				for (int e=head[i];e;e=nxt[e])
    					if (sz[to[e]]==(n>>1)) {ans-=f[to[e]]+1;break;}
    			}else ans-=f[i];
    			printf("%lld
    ",ans);
    		}else puts("-1");
    	return 0;
    }
    

    [BZOJ2527][Poi2011]Meteors

    整体二分模板题。

    BZ的神奇数据居然可以卡爆(mbox{long long})。。。然后我开个(mbox{unsigned long long})就过了?讲道理这里(mbox{unsigned long long})也能爆吧。。。

    #include<cstdio>
    #include<algorithm>
    #include<vector>
    using namespace std;
    int gi(){
    	int x=0,w=1;char ch=getchar();
    	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    	if (ch=='-') w=0,ch=getchar();
    	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    #define ll unsigned long long
    const int N = 300005;
    int n,m,k,p[N],st[N],ed[N],ans[N];
    struct nation{ll p;int id;}q[N],q1[N],q2[N];
    vector<int>a[N];ll val[N],c[N];
    void mdf(int k,ll v){while(k<=m)c[k]+=v,k+=k&-k;}
    ll qry(int k){ll s=0;while(k)s+=c[k],k^=k&-k;return s;}
    void upt(int p,ll f){
    	if (st[p]<=ed[p])
    		mdf(st[p],val[p]*f),mdf(ed[p]+1,-val[p]*f);
    	else
    		mdf(1,val[p]*f),mdf(ed[p]+1,-val[p]*f),mdf(st[p],val[p]*f);
    }
    void solve(int L,int R,int l,int r){
    	if (L>R) return;
    	if (l==r){
    		for (int i=L;i<=R;++i) ans[q[i].id]=l;
    		return;
    	}
    	int mid=l+r>>1,t1=0,t2=0;
    	for (int i=l;i<=mid;++i) upt(i,1);
    	for (int i=L;i<=R;++i){
    		ll tmp=0;
    		for (int j=0,sz=a[q[i].id].size();j<sz;++j)
    			tmp+=qry(a[q[i].id][j]);
    		if (tmp>=q[i].p) q1[++t1]=q[i];
    		else q[i].p-=tmp,q2[++t2]=q[i];
    	}
    	for (int i=l;i<=mid;++i) upt(i,-1);
    	for (int i=L,j=1;j<=t1;++i,++j) q[i]=q1[j];
    	for (int i=L+t1,j=1;j<=t2;++i,++j) q[i]=q2[j];
    	solve(L,L+t1-1,l,mid);solve(L+t1,R,mid+1,r);
    }
    int main(){
    	n=gi();m=gi();
    	for (int i=1;i<=m;++i) a[gi()].push_back(i);
    	for (int i=1;i<=n;++i) q[i]=(nation){gi(),i};
    	k=gi();
    	for (int i=1;i<=k;++i) st[i]=gi(),ed[i]=gi(),val[i]=gi();
    	++k;st[k]=1;ed[k]=m;val[k]=1<<30;
    	solve(1,n,1,k);
    	for (int i=1;i<=n;++i)
    		if (ans[i]==k) puts("NIE");
    		else printf("%d
    ",ans[i]);
    	return 0;
    }
    

    [BZOJ2529][Poi2011]Sticks

    拼成三角形的三根木棍一定是按长度排序后相邻的(不考虑颜色的话),所以就从前往后维护三根木棍就好了。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int gi(){
    	int x=0,w=1;char ch=getchar();
    	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    	if (ch=='-') w=0,ch=getchar();
    	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    #define fi first
    #define se second
    const int N = 1e6+5;
    int k,n;pair<int,int>a[N],b[3];
    int main(){
    	k=gi();
    	for (int i=1;i<=k;++i){
    		int m=gi();
    		while (m--) a[++n]=make_pair(gi(),i);
    	}
    	sort(a+1,a+n+1);
    	for (int i=1;i<=n;++i){
    		if (a[i].se==b[0].se) b[0]=a[i];
    		else if (a[i].se==b[1].se) b[1]=a[i];
    		else if (a[i].se==b[2].se) b[2]=a[i];
    		else b[0]=a[i];
    		sort(b,b+3);
    		if (b[0].se&&b[0].fi+b[1].fi>b[2].fi)
    			return printf("%d %d %d %d %d %d
    ",b[0].se,b[0].fi,b[1].se,b[1].fi,b[2].se,b[2].fi),0;
    	}
    	puts("NIE");return 0;
    }
    

    [BZOJ2530][Poi2011]Party

    随便选两个未被删掉的点,如果两个点之间没有边,那么就把这两个点都删掉。

    因为团的大小有(frac 23n),而删掉的两个点不可能都在团里,所以至少删掉了一个需要删的,那么肯定可以剩下一个大小为(frac 13n)的团。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int gi(){
    	int x=0,w=1;char ch=getchar();
    	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    	if (ch=='-') w=0,ch=getchar();
    	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    const int N = 3005;
    int n,m,k,a[N][N],del[N];
    int main(){
    	n=gi();m=gi();k=n/3;
    	for (int i=1,u,v;i<=m;++i)
    		u=gi(),v=gi(),a[u][v]=a[v][u]=1;
    	for (int i=1;i<=n&&k;++i)
    		for (int j=i+1;j<=n&&k;++j)
    			if (!del[i]&&!del[j]&&!a[i][j])
    				--k,del[i]=del[j]=1;
    	k=0;
    	for (int i=1;i<=n;++i)
    		if (!del[i]){
    			printf("%d ",i);
    			if (++k==n/3) break;
    		}
    	puts("");return 0;
    }
    

    [BZOJ2557][Poi2011]Programming Contest

    直观的想法是直接跑费用流,但是会(TLE)

    可以跑匈牙利,按罚时的先后顺序增广每一个点,匈牙利可以保证之前的匹配点不会被改变,所以这样跑是正确的。

    注意增广一次的最坏复杂度是(O(k))也就是(O(nm))的,如果对每条边都增广一次复杂度就变成(O(n^2m^2))了。可以对每个点标记一下是否增广失败过,然后如果之前就已经增广失败了这次就没必要再跑了。这样最多成功增广(m)次,每个点最多增广失败一次,复杂度就是(O((n+m)nm))

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int gi(){
    	int x=0,w=1;char ch=getchar();
    	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    	if (ch=='-') w=0,ch=getchar();
    	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    const int N = 505;
    int n,m,r,t,k,cnt,to[N*N],nxt[N*N],head[N],d[N],mat[N],vis[N],tim,ban[N],ans1,ans2;
    pair<int,int>a[N*N];
    bool dfs(int u){
    	for (int e=head[u];e;e=nxt[e]){
    		int v=to[e];
    		if (vis[v]==tim) continue;vis[v]=tim;
    		if (!mat[v]||dfs(mat[v])) return mat[v]=u,true;
    	}
    	return false;
    }
    int main(){
    	n=gi();m=gi();r=gi();t=gi();k=gi();
    	for (int i=1;i<=k;++i){
    		int u=gi(),v=gi();
    		to[i]=v;nxt[i]=head[u];head[u]=i;
    		if (d[u]<t/r) a[++cnt]=make_pair(++d[u],u);
    	}
    	sort(a+1,a+cnt+1);
    	for (tim=1;tim<=cnt;++tim)
    		if (!ban[a[tim].second]&&dfs(a[tim].second))
    			++ans1,ans2+=a[tim].first;
    		else ban[a[tim].second]=1;
    	printf("%d %lld
    ",ans1,1ll*ans2*r);
    	return 0;
    }
    
  • 相关阅读:
    Python LED
    vmvare虚拟机经验
    Debian系统简要说明
    Android onclick监听事件打开新界面
    在国外搭个人服务器(顺便访问外网)
    生成指定时间内的 随机日起
    生成指定时间内的日期
    Mac 上查找javahome
    java 学习的网站
    Java 线程
  • 原文地址:https://www.cnblogs.com/zhoushuyu/p/9741916.html
Copyright © 2011-2022 走看看