• [WC2018]州区划分


    题解

    • 首先推出状压dp形式,然后将dp方程中分母处的当前状态人口的总和这一项移到等式左边,会发现dp方程形成一个“自己等于自己卷人口”的形式,然后子集卷积刚好可以解决。
    • 注意子集卷积枚举两边1的个数时人口数组必须至少有一个1才能正确转移,不能同层转移。
    #include<bits/stdc++.h>
    #define ll long long
    #define ull unsigned long long
    #define rep(i, s, t) for(int i = s, __ = t; i <= __; ++i)
    #define dwn(i, s, t) for(int i = s, __ = t; i >= __; --i)
    
    const int INF = 2147483647;
    const int MAXN = 31;
    const int MAXM = 100 + 2500000;
    const int MOD = 998244353;
    using namespace std;
    inline int read(int x = 0, int f = 1){
        char ch = getchar();
    	for(; !isdigit(ch); ch = getchar())if(ch == '-')f = -1;
        for(; isdigit(ch); ch = getchar())x = ch - '0' + x * 10;
    	return x * f;
    }
    inline void write(int x){
    	if(x >= 10)write(x / 10); putchar(x % 10 + '0'); return ;
    }
    
    inline int add(int x, int y){x += y; return x >= MOD ? x - MOD : x;}
    inline int sub(int x, int y){x -= y; return x < 0 ? x + MOD : x;}
    inline void inc(int &x, int y){x += y, x -= x >= MOD ? MOD : 0; return ;}
    inline void dec(int &x, int y){x -= y, x += x < 0 ? MOD : 0; return ;}
    inline int mul(int x, int y){return (ll)x * y % MOD;}
    inline int fpow(int x, int y, int tmp = 1){
    	while(y){if(y & 1)tmp = mul(tmp, x); x = mul(x, x), y >>= 1;} return tmp;
    }
    inline int inv(int x){return fpow(x, MOD - 2);}
    inline int die(int x, int y){return (ll)x * inv(y) % MOD;}
    
    int n, m, p, lim, num = -1, fs[MAXN], q[MAXN];
    struct List{int u, v;
    	void get(){u = read(), v = read(); return ;}
    }Edge[MAXN * MAXN >> 1];
    int f[MAXN], can[MAXM], vis[MAXN], du[MAXN];
    int s[MAXN][MAXM], dp[MAXN][MAXM], bc[MAXM], sum[MAXM];
    int find(int x){return f[x] == x ? x : find(f[x]);}
    int check(int now, int rec = 0){
    	rep(i, 1, n)vis[i] = (now >> i - 1) & 1;
    	rep(i, 1, n)du[i] = 0, f[i] = i, rec = vis[i] ? i : rec;
    	if(bc[now] != 1)rep(i, 1, n)inc(sum[now], vis[i] * q[i]);
    	rep(i, 1, m)if(vis[Edge[i].u] && vis[Edge[i].v]){
    		du[Edge[i].u]++, du[Edge[i].v]++;
    		f[find(Edge[i].u)] = f[find(Edge[i].v)];
    	}
    	rep(i, 1, n)if(vis[i])
    		if((find(i) != find(rec)) || (du[i] % 2))return 1;
    	return 0;
    }
    
    void fmt(int *u, int tag){
    	for(int i = 1; i < lim; i <<= 1)rep(j, 0, lim)
    		if(i & j)inc(u[j], ~tag ? u[j - i] : sub(0, u[j - i]));
    	return ;
    }
    
    void init(){
    	memset(fs, -1, sizeof(fs));
    	n = read(), m = read(), p = read();
    	lim = (1 << n) - 1;
    	rep(i, 1, m)Edge[i].get(); rep(i, 1, n)q[i] = read();
    	rep(now, 1, lim)bc[now] = bc[now >> 1] + (now & 1);
    	rep(now, 1, lim)can[now] = check(now);
    	rep(now, 0, lim){
    		sum[now] = fpow(sum[now], p);
    		if(can[now])s[bc[now]][now] = sum[now];
    		sum[now] = inv(sum[now]);
    	}
    	return ;
    }
    
    void work(){
    	rep(now, 0, lim)dp[1][now] = (bc[now] == 1) * can[now];
    	rep(i, 1, n)fmt(s[i], 1);
    	dp[0][0] = 1, fmt(dp[0], 1); fmt(dp[1], 1);
    	rep(i, 2, n){
    		rep(j, 0, i - 1)rep(now, 0, lim)
    			inc(dp[i][now], mul(dp[j][now], s[i - j][now]));
    		fmt(dp[i], -1);
    		rep(now, 0, lim)if(bc[now] != i)dp[i][now] = 0;
    			else dp[i][now] = mul(dp[i][now], sum[now]);
    		if(i != n)fmt(dp[i], 1);
    	}
    	write(dp[n][lim]); return ;
    }
    
    int main(){
    	init(); work(); return 0;
    }
    
  • 相关阅读:
    网络编程学习笔记:Socket编程
    C# url 路径转换 相对路径 转换为 绝对路径
    利用pycharm运行scrapy以及scrapy的配置
    基于scrapy的分布式爬虫抓取新浪微博个人信息和微博内容存入MySQL
    LeetCode 1. Two Sum
    【转载】C#异常Retry通用类
    【转载】WebDriver(C#)之十点使用心得
    c#值类型和引用类型
    percona5.7 源码安装
    android UI进阶之用ViewPager实现欢迎引导页面[转]
  • 原文地址:https://www.cnblogs.com/gdc-destinies/p/11252752.html
Copyright © 2020-2023  润新知