zoukankan      html  css  js  c++  java
  • [WC2018]州区划分

    题意

    题面

    给一张带点权的无向图

    要求对其划分为联通且不存在欧拉回路的多个子图

    定义一个子图的贡献是

    (i)个子图的点权和占前(i)个子图的点权和的比例的(p)次幂

    定义一个划分的贡献是

    该划分下所有子图的贡献的乘积

    求所有划分的贡献之和

    题解

    (f_S)为选取点集为(S)时所有划分的贡献和

    [f_S=frac{sum_{T in S}{f_{S-T}*Sum_T^p}}{Sum_S^p} ]

    注意要求(T)合法

    可以枚举预处理

    只需保证联通(并查集)

    且没有欧拉回路(所有点度数为偶数)

    至此复杂度(O(3^n))

    考虑优化

    分子部分显然是集合卷积的形式

    但与普通的或卷积不同

    这个式子要求两个集合交集为空才能转移

    这里有一个大智若愚、苦尽甘来、有舍有得、其实就是我太菜了的做法

    加一维状态(i)表示所选点集的大小

    这样复杂度就乘上一个$n^2 $啦

    就可以直接或卷积转移

    具体说就是

    枚举点集(S)(S-T)大小

    再直接FWTFMT转移

    这样如果两个集合有交集贡献将是(0)

    复杂度(O(n^22^n))

    #include<bits/stdc++.h>
    
    using namespace std;
    
    #define gc c=getchar()
    #define r(x) read(x)
    #define ll long long 
    
    template<typename T>
    inline void read(T&x){
        x=0;T k=1;char gc;
        while(!isdigit(c)){if(c=='-')k=-1;gc;}
        while(isdigit(c)){x=x*10+c-'0';gc;}x*=k;
    }
    
    const int N=25;
    const int S=1<<21|7;
    const int p=998244353;
    
    struct Edge{
    	int u,v;
    }E[N*N];
    
    int fa[N];
    
    int find(int x){
    	return x==fa[x]?x:fa[x]=find(fa[x]);
    }
    
    inline void uni(int u,int v){
    	u=find(u),v=find(v);
    	if(u!=v)fa[u]=v;
    }
    
    inline bool in(int S,int x){
    	return S>>(x-1)&1;
    }
    
    int n,m,q;
    
    int deg[N];
    
    inline bool check(int S){
    	for(int i=1;i<=n;++i){
    		fa[i]=i;
    		deg[i]=0;
    	}
    	for(int i=1;i<=m;++i){
    		int u=E[i].u,v=E[i].v;
    		if(in(S,u)&&in(S,v)){
    			uni(u,v);
    			++deg[u];
    			++deg[v];
    		}
    	}
    	int lst=0;
    	for(int i=S;i;i^=i&(-i)){
    		int x=__builtin_ffs(i);
    		if(deg[x]&1)return 1;
    		if(lst&&find(x)!=lst)return 1;
    		lst=find(x);
    	}
    	return 0;
    }
    
    inline void add(int &a,int b){
    	a+=b;
    	if(a>=p)a-=p;
    }
    
    inline void sub(int &a,int b){
    	a-=b;
    	if(a<0)a+=p;
    }
    
    inline ll qpow(ll a,ll b){
    	ll ans=1;
    	while(b){
    		if(b&1)(ans*=a)%=p;
    		(a*=a)%=p;
    		b>>=1;
    	}
    	return ans;
    }
    
    inline void fmt(int *A){
    	for(int i=0;i<n;++i){
    		for(int j=0;j<1<<n;++j){
    			if(j>>i&1)add(A[j],A[j^(1<<i)]);
    		}
    	}
    }
    
    inline void ifmt(int *A){
    	for(int i=0;i<n;++i){
    		for(int j=0;j<1<<n;++j){
    			if(j>>i&1)sub(A[j],A[j^(1<<i)]);
    		}
    	}
    }
    
    int w[N];
    int sum[S];
    int Inv[S];
    int bit[S];
    int g[N][S];
    int f[N][S];
    
    int main(){
    	r(n),r(m),r(q);
    	for(int i=1;i<=m;++i){
    		int u,v;r(u),r(v);
    		E[i]=Edge{u,v};
    	}
    	for(int i=1;i<=n;++i){
    		r(w[i]);
    	}
    	int S=(1<<n)-1;
    	for(int i=1;i<=S;++i){
    		sum[i]=w[__builtin_ffs(i)]+sum[i^(i&(-i))];
    		bit[i]=bit[i>>1]+(i&1);
    		int tmp=qpow(sum[i],q);
    		g[bit[i]][i]=check(i)*tmp;
    		Inv[i]=qpow(tmp,p-2);
    	}
    	for(int i=1;i<=n;++i)fmt(g[i]);
    	f[0][0]=1;fmt(f[0]);
    	for(int i=1;i<=n;++i){
    		for(int j=0;j<i;++j){
    			for(int k=0;k<1<<n;++k){
    				add(f[i][k],(ll)f[j][k]*g[i-j][k]%p);
    			}
    		}
    		ifmt(f[i]);
    		for(int k=0;k<1<<n;++k)f[i][k]=i==bit[k]?(ll)f[i][k]*Inv[k]%p:0;
    		if(i^n)fmt(f[i]);
    	}
    	printf("%d
    ",f[n][S]);
    }
    
    
  • 相关阅读:
    Linux系统下用mail(mailx)发送邮件
    Docker+Nginx+KeepaLived 简单实现Nginx高可用+负载均衡
    MySql安装(Windows版本)
    十分钟入门Git和GitHub的源码版本管理
    Sql Server数据库读写分离配置
    C 语言中的 printf() 和 scanf() 简介
    C 语言字符串简介
    C 语言概述
    初识 C 语言
    计算机中的存储器
  • 原文地址:https://www.cnblogs.com/yicongli/p/10185234.html
Copyright © 2011-2022 走看看