bzoj 4694 水题嘉年华 - 数据分治

题解:
首先两个直观的做法是,如果序列中-1很少,那么枚举-1是怎么配对的,然后二分图染色一下即可。另一种情况是,枚举所有已知的配对的上下情况,对于其余的-1,若其向上连视为1,向下连视为0,那么等价于有若干区间和为奇数/偶数的限制,而且只要满足这些限制就必然能构造一组解。最终,大约当-1的个数/2在[0,5~7]时跑第一种,其余跑第二种算法的时候,能够得到100分的好成绩。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;const int N=1000,M=100000;int a[N];
namespace task1{
	int n,has_ans,col[N],h[N],etop,L[N],R[N];struct edges{ int to,pre; }e[M];
	inline int add_edge(int u,int v) { return e[++etop].to=v,e[etop].pre=h[u],h[u]=etop; }
	inline int build_edge(int u,int v) { return add_edge(u,v),add_edge(v,u); }
	inline int getv(int s,int i) { return (s>>(i-1))&1; }
	int paint(int x,int las=1)
	{
		col[x]=3-las;
		for(int i=h[x],y;i;i=e[i].pre)
			if(!col[y=e[i].to]) { if(!paint(y,col[x])) return 0; }
			else if(col[y]==col[x]) return 0;return 1;
	}
	inline int check()
	{
		int m=n/2;
		memset(col,0,sizeof(int)*(n+1));int can=1;
		memset(h,0,sizeof(int)*(n+1)),etop=0;
		rep(i,1,m) rep(j,i+1,m)
		{
			int Li=L[i],Ri=R[i],Lj=L[j],Rj=R[j];
			if(Li>Lj) swap(Li,Lj),swap(Ri,Rj);
			if(Ri>Lj&&Ri<Rj) build_edge(i,j);
		}
		rep(i,1,m) if(!col[i]) if(!paint(i)) { can=0;break; }
		return can;
	}
	int dfs(int x,int n,int m)
	{
		if(has_ans) return 0;if(x>n) return has_ans|=check();if(a[x]) return dfs(x+1,n,m);
		rep(i,x+1,n) if(!a[i]) a[x]=a[i]=m+1,L[m+1]=x,R[m+1]=i,dfs(x+1,n,m+1),a[x]=a[i]=0;
		return 0;
	}
	inline int solve()
	{
		rep(i,1,n) L[i]=n+1,R[i]=-1;int m=0;
		rep(i,1,n) if(a[i]) L[a[i]]=min(L[a[i]],i),R[a[i]]=max(R[a[i]],i),m++;
		return has_ans=0,dfs(1,n,m/2),!printf(has_ans?"POSSIBLE\n":"IMPOSSIBLE\n");
	}
}
namespace task2{
	int n,col[N],L[N],R[N],h[N],etop,P[N],vis[N],zcnt[N];struct edges{int to,pre,wgt;}e[M];
	inline int add_edge(int u,int v,int w) { return e[++etop].to=v,e[etop].pre=h[u],e[etop].wgt=w,h[u]=etop; }
	inline int build_edge(int u,int v,int w) { return add_edge(u,v,w),add_edge(v,u,w); }
	inline int getv(int s,int i) { return (s>>(i-1))&1; }
	int dfs(int x,int c=0)
	{
		col[x]=c,vis[x]=1;
		for(int i=h[x],y;i;i=e[i].pre)
			if(!vis[y=e[i].to]) { if(!dfs(y,c^e[i].wgt)) return 0; }
			else if(col[y]!=(col[x]^e[i].wgt)) return 0;
		return 1;
	}
	inline int solve()
	{
		rep(i,1,n) L[i]=n+1,R[i]=-1;int m=0,has_ans=0;
		rep(i,1,n) if(a[i]) L[a[i]]=min(L[a[i]],i),R[a[i]]=max(R[a[i]],i),m++;
		rep(i,1,n) zcnt[i]=zcnt[i-1]+(a[i]==0);m/=2;int all=(1<<m)-1;
		for(int s=0;s<=all;s+=2)
		{
			int c=0,can=1;
			rep(i,1,m) rep(j,i+1,m) if(getv(s,i)==getv(s,j))
			{
				int Li=L[i],Ri=R[i],Lj=L[j],Rj=R[j];
				if(Li>Lj) swap(Li,Lj),swap(Ri,Rj);
				if(Ri>Lj&&Ri<Rj) { can=0;goto loop; }
			}
			rep(i,0,n) P[i]=++c;memset(h,0,sizeof(int)*(n+2)),etop=0;
			rep(i,1,m)
				if(getv(s,i)) build_edge(P[L[i]-1],P[R[i]],0);
				else build_edge(P[L[i]-1],P[R[i]],getv(zcnt[R[i]]-zcnt[L[i]-1],1));
			rep(i,1,n) if(a[i]) build_edge(P[i-1],P[i],0);
			build_edge(P[0],P[n],0),memset(vis,0,sizeof(int)*(n+2));
			rep(i,1,n+1) if(!vis[i]) if(!dfs(i)) { can=0;break; }
			loop:;if(can) { has_ans=1;break; }
		}
		return !printf(has_ans?"POSSIBLE\n":"IMPOSSIBLE\n");
	}
}
int main()
{
	int T;scanf("%d",&T);
	while(T--)
	{
		int n,k=0;scanf("%d",&n),task1::n=task2::n=n;
		rep(i,1,n) scanf("%d",&a[i]),a[i]++,k+=(a[i]==0);
		static int cnt,v[100];cnt=0,memset(v,0,sizeof(int)*(n+1));
		rep(i,1,n) if(a[i]) { if(!v[a[i]]) v[a[i]]=++cnt;a[i]=v[a[i]]; }
		if(k/2<=5) task1::solve();else task2::solve();
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Mys_C_K/article/details/82968876