传送门:https://codeforces.com/contest/1221/problem/G
sol:
感觉这个G题还挺好搞的……
首先同时有$0$,$1$,$2$正面统计好像不大好统计
那就反过来
总方案数$2^n$
然后要减去没有$0$的方案,没有$1$的方案,没有$2$的方案,加上没有$0,1$的方案,没有$0,2$的方案,没有$1,2$的方案,减去没有$1,2,3$的方案
我们分开讨论
显然 没有$0$的方案数和没有$2$的方案数是等价的
没有$0$的方案:考虑把填$0$设为$1$,其他设为$0$,$2^{40}$好像有点大 拆半找 然后对于一个确定的前半部分 后半部分不能选的方案就确定了,然后枚举后半部分取法 然后加上合法的前半边的计数就行了
没有$1$的方案:即一个联通块内同时填$0$或$1$,设联通块数为$c$,答案为$2^c$
没有$0,1$的方案:即只有2的方案,这个时候可以发现,除了孤点,其他的点都填入$1$,只需要统计孤点的数量,设为$cnt$,答案为$2^cnt$
没有$0,2$的方案:即只有1的方案,也就是说这时候是0101……这样交替填入。如果有奇环则不合法,如果没有奇环,答案为$2^c$,否则是$0$
没有$1,2$的方案:只有0的方案,这时候和只有$2$的方案等价
没有$0,1,2$的方案 如果$m=0$ 答案为$2^n$,否则为$0$
思路大概就这样233
代码可能有点麻烦
Code:
#include <bits/stdc++.h> using namespace std; int N,M; int vis[55]; vector <int> qwq[40]; long long Right[45],cntRight[1<<20]; long long Onl(){ long long ans=0; for (int i=0;i<N;i++) if (qwq[i].size()==0) ans++; return ans; } void dfs(int Now,int x){ if (vis[Now]) return; vis[Now]=x; for (int i=0;i<qwq[Now].size();i++) dfs(qwq[Now][i],3-x); } long long GetComponents(){ memset(vis,0,sizeof(vis)); long long ans=0; for (int i=0;i<N;i++) if (!vis[i]){ ans++; dfs(i,1); } return ans; } long long Get02(){ long long m1=min(N,20); long long m2=N-m1; memset(cntRight,0,sizeof (cntRight)); for (long long i=0;i<(1ll<<m1);i++){ long long Nowmas=0; bool flag=true; for (int j=0;j<m1;j++){ if ((i&(1ll<<j))==0) continue; if (Nowmas&(1ll<<j)) flag=false; Nowmas|=((1ll<<j)|Right[j]); } if (flag) cntRight[Nowmas>>m1]++; } for (int i=0;i<m2;i++) for (int j=0;j<(1<<m2);j++) if (j&(1ll<<i)) cntRight[j]+=cntRight[j^(1ll<<i)]; long long ans=0; for (long long i=0;i<(1<<m2);i++){ long long Nowmas=0; bool flag=true; for (int j=m1;j<N;j++){ if ((i&(1<<(j-m1)))==0) continue; if (Nowmas&(1ll<<j)) flag=false; Nowmas|=(1ll<<j)|Right[j]; } if (flag) ans += cntRight[i^((1ll<<m2)-1)]; } return ans; } bool Non_Odd(){ memset(vis,0,sizeof(vis)); for (int i=0;i<N;i++) if (!vis[i]) dfs(i,1); for (int i=0;i<N;i++){ for (int j=0;j<qwq[i].size();j++) if (vis[i]==vis[qwq[i][j]]) return false; } return true; } long long Pow(long long x,long long y){ long long ans=1; for (;y;y>>=1){ if (y&1) ans=ans*x; x=x*x; } return ans; } long long Calc(int Mask){ if (Mask==0) return Pow(2,N); if (Mask==1||Mask==4) {return Get02();} if (Mask==2){return Pow(2,GetComponents());} if (Mask==3||Mask==6){return Pow(2,Onl());} if (Mask==5){if (Non_Odd()) return Pow(2,GetComponents());else return 0;} if (Mask==7) {if (M==0) return Pow(2,N);else return 0;} } int main(){ scanf("%d%d",&N,&M); for (int i=0;i<M;i++){ int x,y; scanf("%d%d",&x,&y); --x;--y; qwq[x].push_back(y); qwq[y].push_back(x); Right[x]^=(1ll<<y); Right[y]^=(1ll<<x); } long long ans=0; for (int i=0;i<8;i++){ if (__builtin_popcount(i)%2==0) ans+=Calc(i); else ans-=Calc(i); } cout<<ans; return 0; }