[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 }
View Code

 

posted @ 2021-05-13 14:38  PYWBKTDA  阅读(56)  评论(0编辑  收藏  举报