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]$$

这东西是个子集卷积的形式。

但是在卷的时候要调用自己。

那怎么办?

一边做子集卷积,一边得出新答案。

具体地:枚举一下集合大小 S ,每次通过之前的结果做卷积求出当前集合大小的所有集合的答案。

直接保留 FMT 后的结果,方便计算、降低时间复杂度。

具体细节见代码。

时间复杂度 $O(n^22^n)$ 。

代码

#pragma GCC optimize("Ofast","inline")
#include <bits/stdc++.h>
#define clr(x) memset(x,0,sizeof (x))
#define For(i,a,b) for (int i=a;i<=b;i++)
#define Fod(i,b,a) for (int i=b;i>=a;i--)
#define pb(x) push_back(x)
#define mp(x,y) make_pair(x,y)
#define fi first
#define se second
#define _SEED_ (‘C‘+‘L‘+‘Y‘+‘A‘+‘K‘+‘I‘+‘O‘+‘I‘)
#define outval(x) printf(#x" = %d\n",x)
#define outvec(x) printf("vec "#x" = ");for (auto _v : x)printf("%d ",_v);puts("")
#define outtag(x) puts("----------"#x"----------")
#define outarr(a,L,R) printf(#a"[%d...%d] = ",L,R);						For(_v2,L,R)printf("%d ",a[_v2]);puts("");
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef vector <int> vi;
LL read(){
	LL x=0,f=0;
	char ch=getchar();
	while (!isdigit(ch))
		f|=ch==‘-‘,ch=getchar();
	while (isdigit(ch))
		x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return f?-x:x;
}
const int N=23,S=1<<21,mod=998244353;
const ULL Bmod=16ULL*mod*mod;
int Pow(int x,int y){
	int ans=1;
	for (;y;y>>=1,x=(LL)x*x%mod)
		if (y&1)
			ans=(LL)ans*x%mod;
	return ans;
}
void Add(int &x,int y){
	if ((x+=y)>=mod)
		x-=mod;
}
void Del(int &x,int y){
	if ((x-=y)<0)
		x+=mod;
}
int n,m,s,p;
vector <int> e[N];
int w[N];
int cnt1[S],sum[S],f[S];
int g[N][N];
int u[N][S],v[N][S];
int check(int s){
	static int vis[N],in[N],q[N],head,tail,x;
	if (!s)
		return 0;
	clr(vis),clr(in);
	int fir=-1;
	For(i,0,n-1)
		if (s>>i&1){
			fir=i;
			break;
		}
	head=tail=0;
	q[++tail]=fir,vis[fir]=1;
	while (head<tail){
		x=q[++head];
		for (auto y : e[x])
			if (s>>y&1){
				in[y]^=1;
				if (!vis[y])
					vis[y]=1,q[++tail]=y;
			}
	}
	if (tail!=cnt1[s])
		return 1;
	For(i,0,n-1)
		if (in[i])
			return 1;
	return 0;
}
void FMT(int *a){
	For(i,0,n-1)
		For(j,0,s-1)
			if (j>>i&1)
				Add(a[j],a[j^1<<i]);
}
void IFMT(int *a){
	For(i,0,n-1)
		For(j,0,s-1)
			if (j>>i&1)
				Del(a[j],a[j^1<<i]);
}
int main(){
	n=read(),m=read(),p=read();
	s=1<<n;
	clr(g);
	For(i,1,m){
		int x=read()-1,y=read()-1;
		e[x].pb(y),e[y].pb(x);
	}
	For(i,0,n-1)
		w[i]=read();
	For(i,0,s-1){
		For(j,0,n-1)
			if (i>>j&1){
				cnt1[i]++;
				sum[i]+=w[j];
			}
		f[i]=check(i);
		sum[i]=Pow(sum[i],p);
		if (f[i])
			u[cnt1[i]][i]=sum[i];
	}
	For(i,0,n)
		FMT(u[i]);
	v[0][0]=1;
	FMT(v[0]);
	For(i,1,n){
		For(k,0,s-1){
			ULL tmp=0;
			For(j,0,i-1){
				tmp+=(LL)v[j][k]*u[i-j][k];
				if (tmp>=Bmod)
					tmp-=Bmod;
			}
			v[i][k]=tmp%mod;
		}
		IFMT(v[i]);
		For(k,0,s-1)
			if (cnt1[k]==i)
				v[i][k]=(LL)v[i][k]*Pow(sum[k],mod-2)%mod;
			else
				v[i][k]=0;
		FMT(v[i]);
	}
	IFMT(v[n]);
	cout<<v[n][s-1]<<endl;
	return 0;
}

  

原文地址:https://www.cnblogs.com/zhouzhendong/p/UOJ348.html

时间: 2024-07-31 06:14:05

UOJ#348. 【WC2018】州区划分的相关文章

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

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

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]$就是答案,显然这个状态中的每个城市只会出现一次.

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

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)\). 多开一维表示\

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 {

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

uoj#349 【WC2018】即时战略

题目链接 正解:$link-cut \ tree$. 这道题我在考场上从看题到放弃只花了$20$多分钟.. 爆刚$t2$无果,$12$点的钟声响起,我无奈地开始看这道题,然后发现了生的希望.. 只写了二十几分钟,然后又滚回去刚$t2$了..正解根本就没去想了(虽然本来也不会.. 不得不说这道题的标算还是很妙的,我就算去想也不可能往$LCT$这方面想.. 我们每次新开一个点,就直接从根结点开始$explore$. 我们可以用$LCT$维护当前这棵树的链,于是每次从现在$splay$的根结点开始往下

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)