bzoj 4694 水題嘉年華 - 資料分治
阿新 • • 發佈:2018-11-11
題解:
首先兩個直觀的做法是,如果序列中-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;
}