D41 2-SAT P3513 [POI2011] KON-Conspiracy
视频链接:D41 2-SAT P3513 [POI2011] KON-Conspiracy_哔哩哔哩_bilibili
P3513 [POI2011] KON-Conspiracy - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
#include <iostream> #include <cstring> #include <algorithm> using namespace std; const int N=5005,M=N<<1; int head[M],idx; struct edge{int to,ne;}e[N*N]; int dfn[M],low[M],tim,stk[M],top,scc[M],cnt; int n,m; int a[N],an; //存储后勤成员与数量 int b[N],bn; //存储同谋成员与数量 int num[N],id[N]; //存储矛盾点的数量与编号 bool know[N][N],inb[N]; //是否认识,是否在同谋组 void add(int x,int y){ e[++idx]={y,head[x]}; head[x]=idx; } void tarjan(int x){ dfn[x]=low[x]=++tim; stk[++top]=x; for(int i=head[x];i;i=e[i].ne){ int y=e[i].to; 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 main(){ scanf("%d",&n); for(int i=1,x;i<=n;i++){ scanf("%d",&m); while(m--) scanf("%d",&x),know[i][x]=true; } for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++){ if(know[i][j]) add(i+n,j),add(j+n,i); else add(i,j+n),add(j,i+n); } for(int i=1;i<=2*n;i++) if(!dfn[i]) tarjan(i); for(int i=1;i<=n;i++){ if(scc[i]==scc[i+n]) puts("0"),exit(0); if(scc[i]<scc[i+n]) a[++an]=i; //存储后勤成员 else b[++bn]=i,inb[i]=1; //存储同谋成员 } for(int i=1;i<=an;i++) for(int j=1;j<=bn;j++) if(know[a[i]][b[j]]) //后勤认识同谋即矛盾点 ++num[a[i]],id[a[i]]=b[j]; for(int i=1;i<=bn;i++) for(int j=1;j<=an;j++) if(!know[b[i]][a[j]]) //同谋不认识后勤即矛盾点 ++num[b[i]],id[b[i]]=a[j]; int ans=0; for(int i=1;i<=n;i++){ //i有1个矛盾点且i的矛盾点无矛盾点,可以直接交换 if(num[i]==1 && !num[id[i]]) ++ans; //i无矛盾点且本组超过1人,可以直接移到对面组 if(!num[i]&&((inb[i]&&bn>1)||(!inb[i]&&an>1))) ++ans; } if(an&&bn) ++ans; //初始解 printf("%d\n",ans); }