UOJ#348 州区划分

解:有一个很显然的状压......

就设f[s]表示选的点集为s的时候所有方案的权值和。

于是有f[s] = f[s \ t] * (sum[t] / sum[s])P

这枚举子集是3n的。

然后发现这是子集卷积,参考资料

于是就FWT搞一下...看代码

  1 #include <bits/stdc++.h>
  2
  3 typedef long long LL;
  4 const int N = 30, M = 2100000, MO = 998244353;
  5
  6 struct Edge {
  7     int v, u;
  8 }edge[N * N];
  9
 10 int w[N], sum[M], cnt[M], n, P, lm, invsum[M], pw[M], in[N], fa[N];
 11 int f[N][M], g[N][M];
 12 bool vis[M];
 13
 14 int find(int x) {
 15     if(x == fa[x]) return x;
 16     return fa[x] = find(fa[x]);
 17 }
 18
 19 inline void out(int x) {
 20     for(int i = 0; i < n; i++) {
 21         printf("%d", (x >> i) & 1);
 22     }
 23     return;
 24 }
 25
 26 inline void merge(int x, int y) {
 27     fa[find(x)] = find(y);
 28     return;
 29 }
 30
 31 inline int qpow(int a, int b) {
 32     int ans = 1;
 33     while(b) {
 34         if(b & 1) ans = 1ll * ans * a % MO;
 35         a = 1ll * a * a % MO;
 36         b = b >> 1;
 37     }
 38     return ans;
 39 }
 40
 41 inline int pow(int x) {
 42     return P ? (P == 1 ? x : 1ll * x * x % MO) : 1;
 43 }
 44
 45 inline void FWT_or(int *a, int n, int f) {
 46     for(int len = 1; len < n; len <<= 1) {
 47         for(int i = 0; i < n; i += (len << 1)) {
 48             for(int j = 0; j < len; j++) {
 49                 (a[i + len + j] += f * a[i + j]) %= MO;
 50                 if(a[i + len + j] < 0) a[i + len + j] += MO;
 51             }
 52         }
 53     }
 54     return;
 55 }
 56
 57 int main() {
 58     int m;
 59     scanf("%d%d%d", &n, &m, &P);
 60
 61     for(int i = 1, x, y; i <= m; i++) {
 62         scanf("%d%d", &edge[i].v, &edge[i].u);
 63     }
 64     for(int i = 1; i <= n; i++) scanf("%d", &w[i]);
 65     lm = (1 << n) - 1; /// lm = 1111111111(2)
 66     for(int i = 2; i <= lm; i++) pw[i] = pw[i >> 1] + 1;
 67     for(int s = 1; s <= lm; s++) {
 68         cnt[s] = cnt[s ^ (s & (-s))] + 1;
 69         vis[s] = 0;
 70         memset(in + 1, 0, n * sizeof(int));
 71         for(int i = 1; i <= n; i++) {
 72             fa[i] = i;
 73         }
 74         for(int i = 1; i <= m; i++) {
 75             if(((s >> (edge[i].v - 1)) & 1) && ((s >> (edge[i].u - 1)) & 1)) {
 76                 in[edge[i].v]++;
 77                 in[edge[i].u]++;
 78                 merge(edge[i].u, edge[i].v);
 79             }
 80         }
 81         bool nol = 0;
 82         int temp = 0;
 83         for(int i = 1; i <= n; i++) {
 84             if(in[i] & 1) {
 85                 vis[s] = 1;
 86             }
 87             if((s >> (i - 1)) & 1) {
 88                 (sum[s] += w[i]) %= MO;
 89                 if(!temp) {
 90                     temp = find(i);
 91                 }
 92                 else if(find(i) != temp) {
 93                     nol = 1;
 94                 }
 95             }
 96         }
 97         if(nol) vis[s] = 1;
 98         invsum[s] = qpow(sum[s], MO - 2);
 99         if(vis[s]) {
100             g[cnt[s]][s] = pow(sum[s]);
101         }
102     }
103
104     f[0][0] = 1;
105     for(int i = 1; i <= n; i++) {
106         FWT_or(g[i], lm + 1, 1);
107     }
108     FWT_or(f[0], lm + 1, 1);
109     for(int i = 1; i <= n; i++) {
110         for(int j = 1; j <= i; j++) {
111             for(int s = 0; s <= lm; s++) {
112                 (f[i][s] += 1ll * f[i - j][s] * g[j][s] % MO) %= MO;
113             }
114         }
115         FWT_or(f[i], lm + 1, -1);
116         for(int s = 0; s <= lm; s++) {
117             f[i][s] = 1ll * f[i][s] * pow(invsum[s]) % MO;
118         }
119         if(i < n) FWT_or(f[i], lm + 1, 1);
120     }
121     printf("%d\n", f[n][lm]);
122     return 0;
123 }

AC代码

原文地址:https://www.cnblogs.com/huyufeifei/p/10721352.html

时间: 2024-08-30 17:04:59

UOJ#348 州区划分的相关文章

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

[UOJ#348][WC2018]州区划分 试题描述 小 \(S\) 现在拥有 \(n\) 座城市,第ii座城市的人口为 \(w_i\),城市与城市之间可能有双向道路相连. 现在小 \(S\) 要将这 \(n\) 座城市划分成若干个州,每个州由至少一个城市组成,每个城市在恰好一个州内. 假设小 \(S\) 将这些城市划分成了 \(k\) 个州,设 \(V_i\) 是第 \(i\) 个州包含的所有城市组成的集合. 定义一条道路是一个州的内部道路,当且仅当这条道路的两个端点城市都在这个州内. 如果一

UOJ#348. 【WC2018】州区划分

原文链接www.cnblogs.com/zhouzhendong/p/UOJ348.html 前言 第一次知道子集卷积可以自己卷自己. 题解 这是一道子集卷积模板题. 设 $sum[S]$ 表示点集 S 的点权和. 设 $f[S]$ 表示对点集 S 进行州区划分得到的答案,定义 $g[S]$ 在点集 S 合法时为 $(sum[S])^p$,不合法时为 0 . 则 $$f[S] = \frac{1}{(sum[S])^p}\sum_{T\subsetneq S} f[T]g[S-T]$$ 这东西是

UOJ348. 【WC2018】州区划分

UOJ348. [WC2018]州区划分 http://uoj.ac/problem/348 分析: 设\(g(S)=(\sum\limits_{x\in S}w_x)^p[合法]\) \(f(S)\)表示\(S\)集合内的答案. \(f(S)=\sum\limits_{T\subseteq S,|T|>0}g(T)f(S-T)s(S)\). 这玩意可以使用占位多项式搞搞. 大概就是形如\(f(S)=\sum\limits_{P|Q=S,|P|+|Q|=S}g(P)h(Q)\). 多开一维表示\

Luogu4221 WC2018州区划分(状压dp+FWT)

合法条件为所有划分出的子图均不存在欧拉回路或不连通,也即至少存在一个度数为奇数的点或不连通.显然可以对每个点集预处理是否合法,然后就不用管这个奇怪的条件了. 考虑状压dp.设f[S]为S集合所有划分方案的满意度之和,枚举子集转移,则有f[S]=Σg[S']*f[S^S']*(sum[S']/sum[S])p (S'?S),其中g[S]为S集合是否合法,sum[S]为S集合人口数之和.复杂度O(3n).这个式子非常显然,就这么送了50分.p这么小显得非常奇怪但也没有任何卵用. 考虑优化.转移方程写

bzoj5153 [Wc2018]州区划分

题目链接 正解:子集和变换. 考场上只会暴力和$p=0$的情况,还只会$O(2^{n}*n^{3})$的. 然而这题题面出锅,导致考场上一直在卡裸暴力,后面的部分分没写了..听$laofu$说$O(2^{n}*n^{3})$可以过.. 所以直接讲正解.. 我们假设每个城市可以在两个不同集合,那么可以把子集卷积变成或卷积. 我们只要记下当前总共有多少个点,于是考虑设$f[i][S]$表示$i$个点,集合为$S$的方案数. 最后的$f[n][all]$就是答案,显然这个状态中的每个城市只会出现一次.

uoj348【WC2018】州区划分

题目链接 直接讲吨吨吨给的标准做法吧.记\(f(i,j)\)表示各个州(可以重叠)的城市数量之和为i,这些州的并集为j的方案数,反正若有两个州之间有交集最后的\(|j|\)会不等于\(i\).有 \(f(i,s)=\sum_{s1} \sum_{s2}[s1|s2==s] \ f(i-|s2|,s1)*can(s2) (\frac{vals(s2)}{vals(s)})^p\) \(f(i,s)*vals(s)^p=\sum_j \sum_{|s2|=j} \sum_{s1} [s1|s2==s

WC2018 州区划分

题目描述: luogu 题解: 设$f[S]$表示选集合$S$时所有满意度乘积之和,$W[S]$表示集合$S$中选中的$w$之和.显然有这样一个式子:$$f[S]= \frac{1}{W[S]^p} \sum\limits_{T \subseteq S}f[T]*W[S-T]^p*[check(S-T)]$$ 后面$check$的意思是判断$S-T$是否合法. 原题义中不合法的条件是存在一条欧拉回路.那么: 若图不连通则不存在. 若一个点的度数是奇数则不存在 单个点一定存在 这样可以$O(2^n

UVA 348 矩阵链乘

UVA 348 题意: 给出 N 个矩阵(A1,A2,...,An),求完全括号化方案,使得计算乘积(A1A2...An)所需乘法次数最少.并输出方案. 解题: 算法导论是个好东西   讲的很详细~ 假设矩阵 A 和 B 相乘,那 A 的列数必须要和 B 的行数相同,即 若 A 的行列数为(x,y),则 B 须为(y,z), 它们相乘得到一个(x,z)的矩阵:需要乘 xyz 次.题目把相乘的顺序给出了,我们做的就是添加括号了.把所有矩阵用一个序列 P 表示,第 i 个矩阵的行列数为 P(i-1)

13- 整数划分插入乘号积最大(四)

/*                                            整数划分(四)时间限制:1000 ms  |  内存限制:65535 KB难度:3 描述 暑假来了,hrdv 又要留学校在参加ACM集训了,集训的生活非常Happy(ps:你懂得),可是他最近遇到了一个难题,让他百思不得其解,他非常郁闷..亲爱的你能帮帮他吗? 问题是我们经常见到的整数划分,给出两个整数 n , m ,要求在 n 中加入m - 1 个乘号,将n分成m段,求出这m段的最大乘积 输入    第