传送门: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;
}
知识兔