这种比赛时只有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.
#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; }