• [UOJ#348][WC2018]州区划分


    [UOJ#348][WC2018]州区划分

    试题描述

    (S) 现在拥有 (n) 座城市,第ii座城市的人口为 (w_i),城市与城市之间可能有双向道路相连。

    现在小 (S) 要将这 (n) 座城市划分成若干个州,每个州由至少一个城市组成,每个城市在恰好一个州内。

    假设小 (S) 将这些城市划分成了 (k) 个州,设 (V_i) 是第 (i) 个州包含的所有城市组成的集合。 定义一条道路是一个州的内部道路,当且仅当这条道路的两个端点城市都在这个州内。 如果一个州内部存在一条起点终点相同,不经过任何不属于这个州的城市,且经过这个州的每个城市至少一次、所有内部道路都恰好一次的路径(路径长度可以为 (0)),则称这个州是不合法的。

    定义第 (i) 个州的满意度为:第 (i) 个州的人口在前 (i) 个州的人口中所占比例的 (p) 次幂,即:

    [left( frac{sum_{x in V_i} w_x}{sum_{j=1}^i sum_{x in V_j} w_x} ight) ^p ]

    定义一个划分的满意度为所有州的满意度的乘积,求所有合法的划分方案的满意度之和,答案对 (998244353) 取模。

    两个划分 ({V_1 cdots V_k})({C_1 cdots C_s}) 是不同的,当且仅当 (k e s) ,或存在某个 (1 le i le k) ,使得 (V_i e C_i)

    输入

    从标准输入读入数据。

    输入第一行包含三个整数 (n,m,p),表示城市个数、城市之间的道路个数以及题目描述中的常数 (p)

    接下来 (m) 行,每行两个正整数 (u,v),描述一条无向的道路,保证无重边无自环;

    输入第 (m+2) 行有 (n) 个正整数,第 (i) 个正整数表示 (w_i)

    输出

    输出到标准输出。

    输出一行一个整数表示答案在模 (998244353) 意义下的取值。

    即设答案化为最简分式后的形式为 (frac{a}{b}) ,其中 (a)(b) 互质。输出整数 (x) 使得 (bx equiv a mod 998244353)(0 le x < 998244353)。可以证明这样的整数 (x) 是唯一的。

    输入示例1

    3 2 1
    1 2
    2 3
    1 1 1
    

    输出示例1

    1
    

    输入示例2 & 输出示例2

    样例数据下载

    数据规模及约定

    保证对于所有数据有:(0 le n le 21)(0 le m le frac{n imes (n−1)}{2})(0 le p le 2)(1 le w_i le 100)

    测试点 (1 sim 5)(n le 15),每个测试点 (10) 分;

    测试点 (7 sim 9)(nle 21,p=0),每个测试点 (5) 分;

    测试点 (10 sim 13)(n le 21,p=1),每个测试点 (5) 分;

    测试点 (14 sim 15)(n le 21,p=2),每个测试点 (5) 分。

    其实 OJ 上每个点分数都一样……

    题解

    还是从暴力 dp 入手。

    (f(S)) 表示划分集合 (S) 中的城市的所有方案的满意度之和,那么转移就是

    [f(S) = sum_{tS subset S} { f(tS) cdot h(S - tS) cdot left( frac{sum(S - tS)}{sum(S)} ight) ^p } ]

    (h(S)) 表示集合 (S) 是否能够成为一个州,能的话值为 (1),否则值为 (0)(sum(S)) 表示集合 (S) 的人口数目总和。

    接下来我们可以朝着 FWT 的方向去想,不难发现要保证 (tS subset S) 这个条件就是做一个或卷积;但是我们并不能保证每个元素都和与它无交集的元素进行卷积,这时候就需要一个处理,我们给状态加一维。

    (f(i, S)) 表示所有州的城市个数总和为 (i),所有州城市的并集(S) 的划分方案满意度之和。注意,这里的州之间是可以有交集的。

    那么现在就可以进行或卷积了,转移方程如下(令 (A) 表示全集):

    [f(i, S) = sum_{j=1}^i sum_{S_1=0}^A sum_{S_2=0}^A [S_1 cup S_2 = S] cdot [|S_2| = j] cdot h(S_2) cdot f(i-j, S_1) cdot left( frac{sum(S_2)}{sum(S)} ight) ^p \ f(i, S) cdot sum(S)^p = sum_{j=1}^i sum_{S_1=0}^A sum_{S_2=0}^A [S_1 cup S_2 = S] cdot ( [|S_2| = j] cdot h(S_2) cdot sum(S_2)^p ) cdot f(i-j, S_1) ]

    (g(j, S_2) = [|S_2| = j] cdot h(S_2) cdot sum(S_2)^p),上面的式子就变成标准的或卷积形式了。

    而且这样处理并不会因为州之间有交集而导致不能得到正确答案,当 (i = |S|) 的时候,(f(i, S)) 恰好是符合题意的答案。所以最终答案就是 (f(n, A))

    我们如果在卷积的过程中一直保留点值(即不 IFWT 回去),就可以做到 (2^n) 的卷积,这样复杂度就是 (O(n^2 cdot 2^n)) 的了。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    using namespace std;
    #define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
    #define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)
    
    int read() {
    	int x = 0, f = 1; char c = getchar();
    	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    	return x * f;
    }
    
    #define maxn 22
    #define maxm 500
    #define maxs 2097152
    #define MOD 998244353
    #define pii pair <int, int>
    #define x first
    #define y second
    #define mp(x, y) make_pair(x, y)
    #define LL long long
    
    int n, m, p, w[maxn], cnt[maxn], can[maxs], sbit[maxs], inv[maxs], f[maxn][maxs], g[maxn][maxs];
    pii es[maxm];
    
    int fa[maxn];
    int findset(int x) { return x == fa[x] ? x : fa[x] = findset(fa[x]); }
    
    int Pow(int a, int b) {
    	int ans = 1, t = a;
    	while(b) {
    		if(b & 1) ans = (LL)ans * t % MOD;
    		t = (LL)t * t % MOD; b >>= 1;
    	}
    	return ans;
    }
    
    void FWT_or(int *a, int len, int tp) {
    	int n = 1 << len;
    	rep(i, 1, len) for(int j = 0; j < n; j += 1 << i) rep(k, 0, (1 << i >> 1) - 1) {
    		int la = a[j+k], ra = a[j+k+(1<<i>>1)];
    		if(tp > 0) {
    			a[j+k] = (la + ra) % MOD;
    			a[j+k+(1<<i>>1)] = la;
    		}
    		else {
    			a[j+k] = ra;
    			a[j+k+(1<<i>>1)] = (la - ra + MOD) % MOD;
    		}
    	}
    	return ;
    }
    
    int main() {
    	n = read(); m = read(); p = read();
    	rep(i, 1, m) {
    		int a = read() - 1, b = read() - 1;
    		es[i] = mp(a, b);
    	}
    	rep(i, 0, n - 1) w[i] = read();
    	
    	int all = (1 << n) - 1;
    	rep(s, 1, all) {
    		int c = 0, cbit;
    		rep(i, 0, n - 1) if(s >> i & 1) sbit[s] += w[i], c++;
    		cbit = c;
    		inv[s] = Pow(sbit[s], MOD - 2);
    		rep(i, 0, n - 1) fa[i] = i, cnt[i] = 0;
    		rep(i, 1, m) if((s >> es[i].x & 1) && (s >> es[i].y & 1)) {
    			int u = findset(es[i].x), v = findset(es[i].y);	
    			if(u != v) fa[v] = u, c--;
    			cnt[es[i].x]++; cnt[es[i].y]++;
    		}
    		bool has = 0;
    		rep(i, 0, n - 1) if(s >> i & 1) has |= cnt[i] & 1;
    		if(has || c > 1) can[s] = 1, g[cbit][s] = Pow(sbit[s], p);
    	}
    	
    	f[0][0] = 1;
    	FWT_or(f[0], n, 1);
    	
    	rep(i, 0, n) FWT_or(g[i], n, 1);
    	rep(i, 1, n) {
    		rep(j, 1, i) rep(k, 0, all) (f[i][k] += (LL)g[j][k] * f[i-j][k] % MOD) %= MOD;
    		FWT_or(f[i], n, -1);
    		rep(k, 0, all) f[i][k] = (LL)f[i][k] * Pow(inv[k], p) % MOD;
    		if(i < n) FWT_or(f[i], n, 1);
    	}
    	
    	printf("%d
    ", f[n][all]);
    	
    	return 0;
    }
    
  • 相关阅读:
    【python】虎牙直播爬虫项目
    【python】读取文件夹中所有文件名,os.walk( )
    【人工智能】使用百度api批量识别图片上的文字,进阶版
    【人工智能】使用百度api识别图片上的文字
    【python】词云图,超简单入门版
    【python】词云图,轮廓+着色,进阶版
    【python】词云图,进阶版
    【python】词云图,入门版
    【python】前程无忧51job岗位招聘信息爬虫程序,自动翻页,进阶版
    【HTML】使用百度地图api制作地图热力图,带控件,进阶版
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/8450096.html
Copyright © 2020-2023  润新知