• [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,所以$nle 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+i-1);
    72             return 0;
    73         }
    74     }
    75 }
    View Code
  • 相关阅读:
    丑数——剑指offer面试题34
    把整数排成最小的数——剑指offer面试题33
    从1到n整数中1出现的次数——剑指offer面试题32
    各种排序方法及其比较
    scrapy安装
    水仙花数
    分数化小数(decimal)
    子序列的和
    倒三角
    韩信点兵
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/14764481.html
Copyright © 2020-2023  润新知