Codeforces 1221 G Graph And Numbers

题面

 

    这种比赛时只有11个人做出来的题一般来说都是暴难的, 我也不知道我怎么搞出来的www

    看完这个题第一感觉就是要容斥,至少有一条某种边的方案已经比较难求了,而直接算三种边都至少存在一条的方案数就更难了2333

    那么不妨考虑从反面容斥吧

    设把三种边的存在情况表示成三进制的话,1表示至少有一条 ,0表示一条都没有,?表示这种边没有限制,那么容斥可以得到的是 : f[111] = f[???] - (f[0??]+f[?0?]+f[??0]) + (f[00?]+f[0?0]+f[?00]) - f[000]

    证明可以通过二项式系数的关系导出,并且可以推广到N维形式。

    显然等号右边的每个f[]都是比较好求的(但是会涉及很多算法),不过注意一些f[]是恒等的(根据图的对称性可得),所以不用每个f[]都去写一个函数算。算等号右边的f[]贡献了本题的大部分码量,这里就不一个一个说了,相信你们都能想出来的hhhhh

    最后注意一下f[000],当且仅当 m==0 时 f[000]=2^n;否则 f[000]=0。

    我一开始就因为这个WA了,想当然以为不可能每种边都没有(I'm reall a bro in bro),即 f[000]=0.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1200005;
 
int n,m,v[55],p[55],M,f[N];
bool g[55][55];
ll ans,all;
 
inline bool Can(int S,int ad){
    for(int i=0;i<M;i++)
        for(int j=i+1;j<M;j++) if(g[i+ad][j+ad]&&((1<<i)&S)&&((1<<j)&S)) return 0;
    return 1;
}
 
inline void Get1(){
    for(int s=0;s<(1<<M);s++) if(Can(s,0)) f[s]++;
}
 
inline void maintain(){
    for(int i=0;i<M;i++)
        for(int j=0;j<(1<<M);j++) if(!((1<<i)&j)) f[j|(1<<i)]+=f[j];
}
 
inline ll Get2(){
    ll an=0;
    for(int s=0,now,al=(1<<M)-1;s<(1<<(n-M));s++) if(Can(s,M)){
        now=0;
        for(int i=M;i<n;i++) if((1<<(i-M))&s)
            for(int j=0;j<M;j++) if(g[i][j]) now|=1<<j;
        an+=f[al^now];
    }
    return an;
}
 
inline ll solve1(){
/*  meet in the middle:
        一半: 枚举合法二进制并用FMT的处理(类似高维每维值域{0,1}的前缀和)
              映射到所有包含它的二进制上
               
        另一半: 枚举合法二进制,直接找FMT数组对应的位置加就OK了
*/ 
    Get1();
    maintain();
    return Get2();
}
 
int getfa(int x){ return p[x]==x?x:(p[x]=getfa(p[x]));}
 
inline ll solve2(){
    ll an=1;
    for(int i=0;i<n;i++) p[i]=i;
     
    for(int i=0,fa,fb;i<n;i++)
        for(int j=i+1;j<n;j++) if(g[i][j]){
            fa=getfa(i),fb=getfa(j);
            if(fa!=fb) p[fa]=fb;
        }
     
    for(int i=0;i<n;i++) if(v[getfa(i)]!=2) v[p[i]]=2,an<<=1;
     
    return an;
}
 
inline ll solve3(){
    ll an=1;
     
    for(int i=0;i<n;i++)
        for(int j=i+1;j<n;j++) if(g[i][j]) v[i]=v[j]=3;
    for(int i=0;i<n;i++) if(v[i]!=3) an<<=1;
     
    return an;
}
 
bool color(int x,int c){
    v[x]=c;
    for(int i=0;i<n;i++) if(g[x][i])
        if(v[i]==v[x]) return 0;
        else if(v[i]<4&&!color(i,9-c)) return 0;
    return 1;
}
 
inline ll solve4(){
    ll an=1;
     
    for(int i=0;i<n;i++) if(v[i]<4)
        if(!color(i,4)) return 0; else an<<=1;
     
    return an;
}
 
int main(){
    scanf("%d%d",&n,&m),all=(1ll<<n)-1,M=n+1>>1;
    if(!m) ans-=all+1;//000 type
    for(int U,V;m;m--)
        scanf("%d%d",&U,&V),U--,V--,g[V][U]=g[U][V]=1;
     
    ans+=all+1;// ??? type
    ans-=2*solve1();// 0?? and ??0 type , cause its symmetry , we can simply double the ans
    ans-=solve2();// ?0? type
    ans+=2*solve3();// ?00 and 00? type , similar to solve1()
    ans+=solve4();//0?0 type
     
    cout<<ans<<endl;
    return 0;
}

  

posted @   蒟蒻JHY  阅读(306)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
· DeepSeek 解答了困扰我五年的技术问题
· 为什么说在企业级应用开发中,后端往往是效率杀手?
阅读排行:
· Deepseek官网太卡,教你白嫖阿里云的Deepseek-R1满血版
· 2分钟学会 DeepSeek API,竟然比官方更好用!
· .NET 使用 DeepSeek R1 开发智能 AI 客户端
· DeepSeek本地性能调优
· 一文掌握DeepSeek本地部署+Page Assist浏览器插件+C#接口调用+局域网访问!全攻略
点击右上角即可分享
微信分享提示