D42 2-SAT+二进制枚举 P3825 [NOI2017] 游戏
视频链接:D42 2-SAT+二进制枚举 P3825 [NOI2017] 游戏_哔哩哔哩_bilibili
P3825 [NOI2017] 游戏 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
// 2-SAT+二进制枚举 O(2^8*(n+m)) #include <iostream> #include <cstring> #include <algorithm> using namespace std; const int N=100005; int head[N],to[N<<1],ne[N<<1],idx; int dfn[N],low[N],tim,stk[N],top,scc[N],cnt; int n,d,m,pos[10]; //pos:x位置 char s[N]; //地图 struct Rule{int i,j;char x,y;}R[N]; //规则 void add(int a,int b){ to[++idx]=b,ne[idx]=head[a],head[a]=idx; } void tarjan(int x){ dfn[x]=low[x]=++tim; stk[++top]=x; for(int i=head[x];i;i=ne[i]){ int y=to[i]; if(!dfn[y]){ //若y尚未访问 tarjan(y); low[x]=min(low[x],low[y]); } else if(!scc[y]) //若y已访问且未处理 low[x]=min(low[x],dfn[y]); } if(low[x]==dfn[x]){ //若x是SCC的根 ++cnt; for(int y=-1;y!=x;) scc[y=stk[top--]]=cnt; } } int get(int i,char c,int t){ return 'A'+(s[i]-'a'+t)%3==c?i:i+n; } char put(int i,int t){ return 'A'+(s[i]-'a'+t)%3; } bool solve(){ memset(head,0,sizeof head); memset(dfn,0,sizeof dfn); memset(scc,0,sizeof scc); idx=tim=top=cnt=0; for(int k=0;k<m;k++){ int i=R[k].i-1,j=R[k].j-1; char x=R[k].x,y=R[k].y; if(s[i]==x+32) continue; //i不用x车 if(s[j]==y+32) //j不用y车 add(get(i,x,1),get(i,x,2)); //i→i' else{ add(get(i,x,1),get(j,y,1)); //i→j add(get(j,y,2),get(i,x,2)); //j'→i' } } for(int i=0;i<2*n;i++)if(!dfn[i])tarjan(i); for(int i=0;i<n;i++) if(scc[i]==scc[i+n]) return false; for(int i=0;i<n;i++) if(scc[i]<scc[i+n]) putchar(put(i,1)); else putchar(put(i,2)); return true; } int main(){ scanf("%d%d %s %d",&n,&d,s,&m); for(int i=0;i<m;i++) scanf("%d %c %d %c",&R[i].i,&R[i].x,&R[i].j,&R[i].y); for(int i=0,j=0;i<n;i++) if(s[i]=='x') pos[j++]=i; //x位置 for(int i=0;i<1<<d;i++){ for(int j=0;j<d;j++) s[pos[j]]=(i>>j&1)?'a':'b'; //x→a或b if(solve()) return 0; } puts("-1"); return 0; }