[cf908H]New Year and Boolean Bridges
暴力枚举最终图中强连通分量的情况,首先要判定这样划分是否可行,即要求——
1.每一个强连通分量内部没有$xor$的关系
2.任意两个不在同一个强连通分量中的点之间没有$and$的关系
当确定划分(且可行)后,此时每一个强连通分量用一个环来表示,所需边数即点数(但当点数为1时,边数为0),接下来强连通分量之间是$xor$和$or$,不论哪种都是用一条链连起来即可
综上分析,所需边数即$n+点数大于1的强连通分量数-1$
题目也即构造强连通分量的划分方式,在满足前面的条件下,最小化上式
对于第2个条件,可以先将$and$关系的点缩点,并且缩点时也判定第1个条件
(以下为了方便,加了双引号的点指缩点后的点,”点“的点数指其在原图中对应的点个数)
缩完点后,由于点数等于1的强连通分量并不影响答案,所以不妨将点数为1的"点"作为一个强连通分量,显然不劣,同时其满足上述条件且不影响答案,不妨删去这类”点“
接下来,仅剩下$xor$和$or$的边,第2个条件就没有意义,第1个条件即限制某一些点对不能被划分到一个连通块中,当仅保留$xor$的边后,也即独立集
换言之,问题即变为划分为若干个独立集,并最小化独立集的个数(由于删去点数为1的点,现在每一个点实际的点数必然大于1,不存在点数为1的强连通分量)
关于这个问题,即求集合并的卷积,单次卷积复杂度为$o(n2^{n})$,同时只需要在一开始做一次FWT,每一次乘上一个数以及IFWT算出$V$上的值,复杂度都是$o(2^{n})$,一共做$n$次(即枚举独立集个数),总复杂度为$o(n2^{n})$
关于这个$n$,由于删去了点数为1的”点“,每一个”点“的点数至少为2,所以$n\le 23$,可以通过
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 55 4 #define S (1<<23) 5 #define mod 998244353 6 int n,fa[N],vis[N][N],id[N],tot[S],g[S],f[S]; 7 char s[N][N]; 8 int find(int x){ 9 if (fa[x]==x)return x; 10 return fa[x]=find(fa[x]); 11 } 12 void merge(int x,int y){ 13 x=find(x),y=find(y); 14 if (x!=y)fa[x]=y; 15 } 16 int main(){ 17 scanf("%d",&n); 18 for(int i=1;i<=n;i++)scanf("%s",s[i]+1); 19 for(int i=1;i<=n;i++)fa[i]=i; 20 for(int i=1;i<=n;i++) 21 for(int j=i+1;j<=n;j++){ 22 if (s[i][j]!=s[j][i]){ 23 if ((s[i][j]!='O')&&(s[j][i]!='O')){ 24 printf("-1"); 25 return 0; 26 } 27 s[i][j]=s[j][i]='O'; 28 } 29 if (s[i][j]=='A')merge(i,j); 30 } 31 for(int i=1;i<=n;i++) 32 for(int j=i+1;j<=n;j++) 33 if ((find(i)==find(j))&&(s[i][j]=='X')){ 34 printf("-1"); 35 return 0; 36 } 37 for(int i=1;i<=n;i++)tot[find(i)]++; 38 for(int i=1;i<=n;i++) 39 if (tot[i]>1)id[i]=++id[0]; 40 if (!id[0]){ 41 printf("%d",n-1); 42 return 0; 43 } 44 for(int i=1;i<=n;i++) 45 if (tot[find(i)]>1)id[i]=id[find(i)]; 46 for(int i=1;i<=n;i++) 47 for(int j=i+1;j<=n;j++) 48 if ((s[i][j]=='X')&&(id[i])&&(id[j]))vis[id[i]][id[j]]=vis[id[j]][id[i]]=1; 49 f[0]=1; 50 for(int i=1;i<(1<<id[0]);i++){ 51 int x=i-(i&(i-1)); 52 tot[i]=tot[i^x]+1; 53 f[i]=f[i^x]; 54 for(int j=1;j<=id[0];j++) 55 if (x==(1<<j-1)){ 56 for(int k=1;k<=id[0];k++) 57 if ((i&(1<<k-1))&&(vis[j][k]))f[i]=0; 58 } 59 } 60 for(int i=0;i<id[0];i++) 61 for(int j=0;j<(1<<id[0]);j++) 62 if (j&(1<<i))f[j]=(f[j]+f[j^(1<<i)])%mod; 63 for(int i=0;i<(1<<id[0]);i++)g[i]=1; 64 for(int i=1;i<=id[0];i++){ 65 for(int j=0;j<(1<<id[0]);j++)g[j]=1LL*f[j]*g[j]%mod; 66 int ans=0; 67 for(int j=0;j<(1<<id[0]);j++) 68 if ((id[0]-tot[j])&1)ans=(ans+mod-g[j])%mod; 69 else ans=(ans+g[j])%mod; 70 if (ans){ 71 printf("%d\n",n+i-1); 72 return 0; 73 } 74 } 75 }