codeforces gym #101987K -TV ShowGame(2-SAT)
题目链接:
https://codeforces.com/gym/101987
题意:
有长度为$n$的只包含$B,R$的字符串
有m种关系,每个关系说出三个位置的确切字符
这三个位置的字符最多有一个是错的
数据范围:
$1\leq n \leq 5000$
$1\leq m \leq 10000$
分析:
比如给出1B 2R 3B
那么如果1是R,2必须是R,3必须是B
如果2是B,1必须是R,3必须是B
如果3是R,1必须是B,2必须是R
对这些关系建立2—SAT模型
1B的编号为1,1R的编号为1+n
缩点,用tarjin算法把两两互达的点连接在一起,这个联通分量要么全被选择要么全部不选
拓扑排序,从小到大遍历,如果这个点没来过,就标记“选择”,相反点的代表则标记“不选择”,并且传递下去
如果还是没看懂:https://blog.csdn.net/jarjingx/article/details/8521690
AC代码:
#include<bits/stdc++.h> #define ll long long #define pic pair<int,char> using namespace std; const int maxn=1e4+7; int low[maxn],tim[maxn],tot,vis[maxn],top,boss[maxn],sk[maxn],pick[maxn],n,dp[maxn]; vector<int>ve[maxn],ve2[maxn]; void tarjin(int x){ tim[x]=low[x]=++tot;//加入递归的时间和最低递归时间 sk[++top]=x; vis[x]=1; for(int i=0;i<ve[x].size();i++){ int v=ve[x][i]; if(tim[v]==0){ tarjin(v); low[x]=min(low[x],low[v]);//对后面产生影响,那么自己也将属于这个联通图 }else if(vis[v]) low[x]=min(low[x],low[v]);//构成环了,当然和他联通呀 } if(low[x]==tim[x]){ int v; while(1){ v=sk[top--];//出栈 vis[v]=0; boss[v]=x;//找到老大了 if(v==x)break; } } } void dfs(int x){//不选择的标记往后传递 if(pick[x]!=0)return ; // cout<<x<<" "<<-1<<endl; pick[x]=-1; for(int i=0;i<ve2[x].size();i++) dfs(ve2[x][i]); } void tp(){//拓扑排序,这个简单 queue<int>que; for(int i=1;i<=2*n;i++) if(boss[i]==i&&dp[i]==0) que.push(i); while(que.size()){ int v=que.front(); if(pick[v]==0){ // cout<<v<<" "<<1<<endl; pick[v]=1; int w=v; if(w>n)w-=n; else w+=n; dfs(boss[w]); } que.pop(); for(int i=0;i<ve2[v].size();i++){ dp[ve2[v][i]]--; if(dp[ve2[v][i]]==0)que.push(ve2[v][i]); } } } int main(){ int m; scanf("%d %d",&n,&m); for(int i=1;i<=m;i++){ pic zz[3]; for(int j=0;j<3;j++){ scanf("%d %c",&zz[j].first,&zz[j].second); getchar(); } for(int j=0;j<3;j++){ int v1=zz[j].first,v2=zz[j].first+n; if(zz[j].second=='B') swap(v1,v2); for(int k=j+1;k<3;k++){ int w1=zz[k].first,w2=zz[k].first+n; if(zz[k].second=='R') swap(w1,w2); if(w1!=v1)ve[w1].push_back(v1);//选择了v1就选择w1,这里反向建边 if(v2!=w2)ve[v2].push_back(w2); } } } for(int i=1;i<=2*n;i++) if(tim[i]==0)tarjin(i); for(int i=1;i<=n;i++) if(boss[i]==boss[i+n]){ printf("-1\n"); return 0; } for(int i=1;i<=2*n;i++){//缩点后重新建边 for(int j=0;j<ve[i].size();j++){ int a=boss[i],b=boss[ve[i][j]]; if(a!=b){ ve2[a].push_back(b); dp[b]++; } } } tp(); for(int i=1;i<=n;i++){ if(pick[boss[i]]==-1) printf("R"); else printf("B"); } printf("\n"); return 0; }