洛谷3825 [NOI2017]游戏 2-sat
原文链接http://www.cnblogs.com/zhouzhendong/p/8146041.html
题目传送门 - 洛谷3825
题解
我们考虑到地图中x的个数很少,最多只有8个。
所以我们可以考虑穷举。
我们只需要把x变成a和b,这样就涵盖了选择A,B,C的三种情况。
所以我们状压枚举每一个x可以变成什么情况。
然后对于每一种情况,几乎就是2-sat裸题了。
然后我们考虑特殊情况:
如果选了A就得选择B。
如果A的状态不合法,那么显然这条边是不用建立的。
否则:
如果B的状态不合法,那么显然不可以到达A,于是我们将状态A连向他的对立点。
如果B的状态合法,那么我们要连接2条边。一条是A到B的,一条是opp(B)到opp(A)的。(其中opp(x)表示状态x的对立状态)
同学们注意了,Tarjan缩点千万别写错,我已经连续3个程序死在Tarjan缩点上面了。
代码
#include <cstring> #include <cstdio> #include <algorithm> #include <cmath> #include <cstdlib> using namespace std; const int N=500005*2,M=1000005*2; struct Gragh{ int cnt,x[M],y[M],nxt[M],fst[N]; void clear(){ cnt=0; memset(fst,0,sizeof fst); } void add(int a,int b){ x[++cnt]=a,y[cnt]=b,nxt[cnt]=fst[a],fst[a]=cnt; } }g,g2; int n,m,d,pos[10],w[N][3]; int dfn[N],low[N],vis[N],inst[N],st[N],bh[N],top,time,cnt; int o[N],in[N],q[N],res[N],head,tail; char place[N]; struct Limit{ int i,j,hi,hj; }l[M]; void Get_Pos(){ int cnt=0; for (int i=1;i<=n;i++) if (place[i]=='x') pos[++cnt]=i; } int opp(int x){ return x+n*(x<=n?1:-1); } void Tarjan_Prepare(){ top=time=cnt=0; memset(st,0,sizeof st); memset(bh,0,sizeof bh); memset(dfn,0,sizeof dfn); memset(low,0,sizeof low); memset(vis,0,sizeof vis); memset(inst,0,sizeof inst); } void Tarjan(int x){ dfn[x]=low[x]=++time; st[++top]=x; inst[x]=vis[x]=1; for (int i=g.fst[x];i;i=g.nxt[i]) if (!vis[g.y[i]]){ Tarjan(g.y[i]); low[x]=min(low[x],low[g.y[i]]); } else if (inst[g.y[i]]) low[x]=min(low[x],low[g.y[i]]); if (dfn[x]==low[x]){ cnt++; bh[st[top]]=cnt; inst[st[top]]=0; while (st[top--]!=x){ bh[st[top]]=cnt; inst[st[top]]=0; } } } bool check(){ for (int i=1;i<=n;i++) if (bh[i]==bh[i+n]) return 0; else o[bh[i]]=bh[i+n],o[bh[i+n]]=bh[i]; return 1; } void solve(int s){ for (int i=1;i<=d;i++) place[pos[i]]='a'+((s>>(i-1))&1); for (int i=1;i<=n;i++){ if (place[i]=='a')w[i][0]=0,w[i][1]=i,w[i][2]=i+n; if (place[i]=='b')w[i][0]=i,w[i][1]=0,w[i][2]=i+n; if (place[i]=='c')w[i][0]=i,w[i][1]=i+n,w[i][2]=0; } g.clear(); for (int I=1;I<=m;I++){ int i=l[I].i,j=l[I].j,hi=l[I].hi,hj=l[I].hj; if (!w[i][hi]) continue; if (w[j][hj]){ g.add(w[i][hi],w[j][hj]); g.add(opp(w[j][hj]),opp(w[i][hi])); } else g.add(w[i][hi],opp(w[i][hi])); } Tarjan_Prepare(); for (int i=1;i<=n*2;i++) if (!vis[i]) Tarjan(i); if (!check()) return; memset(in,0,sizeof in); g2.clear(); for (int i=1;i<=g.cnt;i++) if (bh[g.x[i]]!=bh[g.y[i]]) g2.add(bh[g.y[i]],bh[g.x[i]]),in[bh[g.x[i]]]++; head=tail=0; for (int i=1;i<=cnt;i++) if (!in[i]) q[++tail]=i; memset(res,0,sizeof res); while (head<tail){ int x=q[++head]; if (!res[x]) res[x]=1,res[o[x]]=-1; for (int i=g2.fst[x];i;i=g2.nxt[i]){ in[g2.y[i]]--; if (!in[g2.y[i]]) q[++tail]=g2.y[i]; } } for (int i=1;i<=n;i++){ int v=res[bh[i]]==1?i:(i+n),c; for (int j=0;j<3;j++) if (w[i][j]==v) c=j; printf("%c",c+'A'); } exit(0); } int main(){ scanf("%d%d%s%d",&n,&d,place+1,&m); for (int i=1;i<=m;i++){ char ch1[3],ch2[3]; scanf("%d%s%d%s",&l[i].i,ch1,&l[i].j,ch2); l[i].hi=ch1[0]-'A',l[i].hj=ch2[0]-'A'; } Get_Pos(); for (int i=0;i<(1<<d);i++) solve(i); printf("-1"); return 0; }