zoukankan      html  css  js  c++  java
  • 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.

    #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;
    }
    

      

  • 相关阅读:
    刷面经笔记2019.02.11
    刷面经笔记2019.02.10
    刷面经笔记2019.02.09
    刷面经笔记2019.02.07
    刷面经笔记2019.02.05
    刷面经笔记2019.01.31
    刷面经笔记2019.01.30
    刷面经笔记2019.01.28
    头条2020届实习生笔试题
    金s办公软件web前端笔试题
  • 原文地址:https://www.cnblogs.com/JYYHH/p/11574118.html
Copyright © 2011-2022 走看看