码迷,mamicode.com
首页 > 其他好文 > 详细

轰炸方案 题解

时间:2020-10-24 10:29:07      阅读:23      评论:0      收藏:0      [点我收藏+]

标签:sum   gis   lan   一个   class   代码   表示   fine   限制   

题目描述

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]);
}

轰炸方案 题解

标签:sum   gis   lan   一个   class   代码   表示   fine   限制   

原文地址:https://www.cnblogs.com/nkxjlym/p/13866750.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!