1. 程式人生 > >bzoj 4694 水題嘉年華 - 資料分治

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; }