POI2011 同谋者 Conspiracy

mac2022-06-30  139

POI2011 同谋者 Conspiracy

题目传送门

题意

\(Bitotia\)偷袭了\(Byteotia\)并占领了很大一块领地。\(Byteotia\)国王\(Byteasar\)正在被占领的领地内组织人们抵抗运动。国王需要选择一部分人来作为这场运动的核心。他们会被分成两部分:一部分人作为同谋者在领地内指挥,另一部分人作为后勤提供援助。这两部分人需要满足以下条件:

后勤组必须两两认识,确保整个组有效率地合作;同谋者必须两两不认识;至少有一个人作为后勤,一个人作为同谋者。

国王希望知道有多少种方案将人们分成两组,以及是否有可能这样分组。

题解

简单题意就是将一个图划分成一个团和一个独立集的方案数。直接计算似乎不可取,我们考虑当前已知一种方案,通过调整两个集合中的人转移到另一种方案。我们记团的集合为\(S\),独立集的集合为\(T\),考虑实际上只有三种转移的方式:

\(S\)中的一个人被分到\(T\)\(T\)中的一个人被分到\(S\)\(S\)中的一个人和\(T\)中的一个人交换

并且容易证明不可能同时有两个人从一个集合分到另一个集合中去。所以我们只要搜出一种答案,那么其余的答案都可以通过这种调整方法得到。搜一个答案是比较简单的,用2-set暴力建图跑\(Tarjan\)就可以了。然后我们考虑方案数的计算,我们记矛盾值\(c_i\)表示对于一个点,如果被分到另一个集合中,会有多少个点与之冲突。冲突定义为两点在同在\(T\)中且有边相连,或同在\(S\)中且无边相连。如果矛盾值大于\(0\),那么我们记\(v_i\)表示\(i\)这个点的任意一个矛盾点。 那么对于第一、二中调整方案,显然只有一个集合中的点数大于1且被分配到另一个集合的点矛盾值为0。对于第三种调整方案,我们记需要交换的点为\(a,b(u \in S,v \in T)\),那么当\(a,b\)的矛盾值都为\(0\)时,显然可以直接交换,当\(a,b\)某一个点的矛盾值为\(1\)时(假设为\(a\)),那么\(v_a\)则必须为\(b\)时,两者才可能交换,此时,如果\(c_b=0\),或者\(c_b=1\)并且\(v_b=a\)时,两者可以交换。容易发现当\(c_a>1\)或者\(c_b>1\)时,两者不能交换。然后暴力计算即可,注意不要重复算贡献。

Code

#include<bits/stdc++.h> using namespace std; const int N=10005; int n,tot,clck,tp,bl,c1,c2; int lk[N],head[N],low[N],dfn[N],ins[N],st[N],block[N],c[N],is[N]; bool vis[N/2][N/2],Mp[N/2][N/2]; vector<int>T,D; struct edge { int to,nxt; }E[25000050]; void Addedge(int u,int v) { E[++tot].to=v;E[tot].nxt=head[u];head[u]=tot; } void Tarjan(int o,int fa) { dfn[o]=low[o]=++clck;ins[o]=1; st[++tp]=o; for(int i=head[o];~i;i=E[i].nxt) { int to=E[i].to; if(!dfn[to]) { Tarjan(to,o); low[o]=min(low[o],low[to]); } else if(ins[to]) low[o]=min(low[o],dfn[to]); } if(dfn[o]==low[o]) { ++bl; while(tp) { int x=st[tp];--tp; ins[x]=0; block[x]=bl; if(x==o) break; } } } int main() { memset(head,-1,sizeof head); scanf("%d",&n); for(int i=1,k;i<=n;i++) { scanf("%d",&k); for(int j=1,x;j<=k;j++) { scanf("%d",&x); lk[x]=1; Mp[i][x]=Mp[x][i]=1; } for(int j=1;j<=n;j++) { if(i==j) continue; if(lk[j]) { Addedge(i+n,j); } else { Addedge(i,j+n); } lk[j]=0; } } for(int i=1;i<=n*2;i++) { if(!dfn[i]) Tarjan(i,-1); } for(int i=1;i<=n;i++) { if(block[i]==block[i+n]) return puts("0"),0; if(block[i]<block[i+n]) T.push_back(i); else D.push_back(i); } for(int i=0;i<(int)T.size();i++) { for(int j=0;j<(int)D.size();j++) { if(Mp[T[i]][D[j]]) { c[T[i]]++; if(!is[T[i]]) is[T[i]]=D[j]; } else { c[D[j]]++; if(!is[D[j]]) is[D[j]]=T[i]; } } } for(int i=0;i<(int)T.size();i++) if(!c[T[i]]) c1++; for(int i=0;i<(int)D.size();i++) if(!c[D[i]]) c2++; int ans=(T.size()>=1&&D.size()>=1); ans+=c1*c2; for(int i=0;i<(int)T.size();i++) { if(c[T[i]]!=1) continue; int Ano=is[T[i]]; if(vis[T[i]][Ano]) continue; if(!c[Ano]) { vis[T[i]][Ano]=vis[Ano][T[i]]; ans++; } else if(c[Ano]==1&&is[Ano]==T[i]) { vis[T[i]][Ano]=vis[Ano][T[i]]; ans++; } } for(int i=0;i<(int)D.size();i++) { if(c[D[i]]!=1) continue; int Ano=is[D[i]]; if(vis[D[i]][Ano]) continue; if(!c[Ano]) { vis[D[i]][Ano]=vis[Ano][D[i]]; ans++; } else if(c[Ano]==1&&is[Ano]==D[i]) { vis[D[i]][Ano]=vis[Ano][D[i]]; ans++; } } if(T.size()>1) { for(int i=0;i<(int)T.size();i++) { if(!c[T[i]]) ans++; } } if(D.size()>1) { for(int i=0;i<(int)D.size();i++) { if(!c[D[i]]) ans++; } } printf("%d\n",ans); return 0; }

转载于:https://www.cnblogs.com/Apocrypha/p/10445863.html

相关资源:JAVA上百实例源码以及开源项目
最新回复(0)