zoukankan      html  css  js  c++  java
  • [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
  • 相关阅读:
    使用Visual Studio给SharePoint列表添加Event Receiver
    使用客户端对象模型回写SharePoint列表
    思考:通过源码进行安装的前提是什么
    思考:学习一门语言真的是仅仅只有为了使用吗?或者说学习一个语言如果在工作中不使用,那么学习它还有什么好处和价值呢?
    思考:(使用下载工具比如yum,maven等)下载(库或者jar)到本地都需要设置源
    思考:代码块的大小是进行代码抽出一个方法或者进行重构或者是否在设计时设计为一个方法的衡量因素之一吗?
    思考:对源的包装是为了更好的隔离和在中间插入一些层?
    Spring Cloud和Spring Boot的认识
    如何对待基础知识:
    为什么总觉得事情应付不过来,总觉得事情需要花费很多时间?
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/14764481.html
Copyright © 2011-2022 走看看