轰炸方案 题解

Posted nkxjlym

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了轰炸方案 题解相关的知识,希望对你有一定的参考价值。

题目描述

A国共有 n 个城市,每两个城市之间均有一条交通线联通。如今A国遭到 B 国的重创,岌岌可危。B 国国王决定轰炸A国的交通线。
面对危难之时,A国国王决定更换首都。在 B国的轰炸结束之后,A国的领土将会分成若干个联通 块。A国的首都,将会从联通块大小最大的联通块中,随机选择一个城市,作为首都。
B 国有多少种不同的轰炸方案,使得A国首都所在的联通块大小恰好为 k。两种轰炸方案是不同 的,当且仅当一条交通线在一种方案中存在,在另一种方案中被轰炸。由于方案数可能很大,你需要输 出方案数对 998,244,353 取模的结果。

简要题意

求n个点的图,最大的连通块恰好为k个的数量。点有编号,两张图不同当且仅当存在两点,其中一张图有边连接,另一图无边。

题目分析

首先,确定这道题用dp做。题目让我们求最大的连通块恰好为k,考虑我们通常的dp过程,由于一张图可能有多个连通块,若要限定某一个块恰好为k并不好处理,但是限定所有的块小于等于k是比较好做到的。于是,这里需要用到一个技巧:设(g_k[n])表示n个点的图,最大的连通块小于等于k,那么我们分别把小于等于k的方案(g_k[n])和小于等于k-1的方案(g_{k-1}[n])算出来,用前者减去后者即可得到答案。

问题转化:求n个点的图,最大的连通块小于等于k个的数量。

再次简化问题,考虑n=k时怎么做。实质上就是没有了k的限制,求n个点的连通图的数量(f[n])。理性思考亿下,发现好像并不能直接通过之前算过的f来推出现在的f。如果是(O(n))递推,假设新来了一个点i,不论之前的点是否连通,似乎都有连法使它们连通;如果是(O(n^2))递推,用(f[m])(f[n-m])去推(f[n])好像会算重......
那能不能用总方案减去不连通图的数量呢?总方案(h[n])(C_n^2)条边是否选择来算,那么考虑如何计算不连通图的数量。只要一张图有两个以上的连通块就不连通了,于是我们考虑枚举一个连通块大小为j,而剩下的i-j个点没有限制,尝试写出递推式:

[f[i]=h[i]-sum_{j=1}^{i-1} C_i^jf[j]h[i-j] ]

再次理性思考亿下,好像不太对。
为了体现出点有编号,我们在里面加了一个组合数(C_i^j),表示在i个点里面拿j个点出来连通。但注意到另一边i-j个点是没有限制的,可能就在其中,有一个连通块S大小也为j,当前面的组合数中也枚举到这j个点的时候,就算重了。于是,我们得想个办法,让左边和右边不会有同一个连通块。
如果能让我们通过组合数枚举出来的连通块比较“特殊”就好了。对于当前的图的一个点p,我们只枚举包含p的连通块。也就是说,我们要在i个点里面拿j个点出来连通,先拿一个点p出来,再从i-1个点里选j-1个点与p连通。因为枚举的连通块一定包含p,所以剩下的i-j个点不可能有包含p的连通块,也就不会算重了。
真正的递推式:

[f[i]=h[i]-sum_{j=1}^{i-1} C_{i-1}^{j-1}f[j]h[i-j] ]

有了上面的铺垫,g_k[i]也就很好求了:

[g_k[i]=sum_{j=1}^{min(i,k)} C_{i-1}^{j-1}f[j]g_k[i-j] ]

这里的分析与f的递推式大同小异,就不再赘述了。

这道题还是算比较难的计数dp,主要是要善于对问题进行转化,这道题运用的许多技巧也比较常见,但是我看题解都看了1个多小时......还是得多积累经验啊!

代码

#include <bits/stdc++.h>
#define I inline
#define R register int
#define ll unsigned long long
using namespace std;
const ll P=998244353,N=2003;
ll f[N],h[N],g[N],_g[N],C[N][N];
I void pls(ll &a,ll b){a+=b;if(a>=P)a-=P;}
int main()
{
	int n,k;
	scanf("%d%d",&n,&k);
	for(R i=0;i<=n;i++)
		for(R j=0;j<=i;j++)
		{
			if(j==0)C[i][j]=1;
			else
			{
				C[i][j]=C[i-1][j];
				pls(C[i][j],C[i-1][j-1]);
			}
		}
	_g[0]=g[0]=h[0]=1;
	register ll w=1;
	for(R i=1;i<=n;i++)
	{
		h[i]=h[i-1]*w%P;
		w<<=1;if(w>=P)w-=P;
	}
	for(R i=1;i<=n;i++)
	{
		ll res=0;
		for(R j=1;j<i;j++)
			pls(res,f[j]*C[i-1][j-1]%P*h[i-j]%P);
		f[i]=h[i];pls(f[i],P-res);
	}
	for(R i=1;i<=n;i++)
		for(R j=1;j<=i&&j<=k;j++)
		{
			ll t=f[j]*C[i-1][j-1]%P;
			pls(g[i],t*g[i-j]%P);
			if(j<k)pls(_g[i],t*_g[i-j]%P);
		}
	pls(g[n],P-_g[n]);
	printf("%llu",g[n]);
}






以上是关于轰炸方案 题解的主要内容,如果未能解决你的问题,请参考以下文章

网站漏洞检测修复 短息轰炸漏洞检测与修补方案

python 代码片段和解决方案

微服务架构如何设计,才能挨过“双11”的轰炸?

解决方案电影标题中缺少代码的片段,完成挑战更多[关闭]

JS自动微信消息轰炸

Chrome-Devtools代码片段中的多个JS库