【THUWC2019模拟2019.1.18】Counting

【THUWC2019模拟2019.1.18】Counting

(File IO): input:counting.in output:counting.out
Time Limits: 2000 ms Memory Limits: 1048576 KB Detailed Limits

Description
羽月最近发现,她发动能力的过程是这样的:
构建一个 V 个点的有向图 G,初始为没有任何边,接下来羽月在脑中构建出一个长度为 E 的边的序列,序列中元素两两不同,然后羽月将这些边依次加入图中,每次加入之后计算当前图的强连通分量个数并记下来,最后得到一个长度为E 的序列,这个序列就是能力的效果。
注意到,可能存在边的序列不同而能力效果相同的情况,所以羽月想请你帮她计算能发动的不同能力个数,答案对 998244353 取模。你需要对于1<=E<=V*(V-1)的所有 E 计算答案。

Input
一行一个整数 V。

Output
一行 V*(V-1)个整数,用空格隔开,第 i 个整数表示 E=i 的答案。

Sample Input
4

Sample Output
1 2 4 9 20 39 64 89 114 114 114 114
【样例说明】
数据中的 p 为 0.3
1×3×(0.3×0.3×0.7)+3×0.3×0.3×0.3=0.27
贡献为 1 的情况有三种
贡献为 3 的情况有一种

Data Constraint
对于 10%的数据,1<=V<=5
对于 30%的数据,1<=V<=20
对于 60%的数据,1<=V<=50
对于 100%的数据,1<=V<=100

感想

最近做了一道题目,感觉收获挺大
1、题目要自己想,做题的时候几次想点开题解,最后还是自己解决了问题,感觉不错
2、遇到困难的时候要换一个角度,多从题目性质入手
3、端正心态,不要因为没有去冬令营而灰心丧气

题解

看到这道题先读懂题意
发现这是让我们求出可以构造出来的所有联通分量的序列个数
乍一看无法入手,仔细想一想可以归纳一些繁杂的情况总结为一种情况来覆盖
考虑已经对于一种加边方案来讲,有哪些加边的方法会得到不一样的答案。
用数学归纳法:
我们可以在一个与当前块不连通的地方增加一条边,使得联通块的个数减少一。
然而你发现,这样做似乎是与你从当前的联通块的最大编号的点往前连接所得到的方案会重复;假设之前块中有p个强连通,那么就可以相应的减少1~p个强连通分量。
或者,我们可以浪费一条边
有两种方式可以浪费:1、在前边随便把两个点连上又不改变联通分量的数目
2、往后任意连一条边

发现把前边的任意两个点连上可以转换为把所有的边都连完之后的随意连边时间
所以浪费边只有往后任意连一条边的情况
问题在与往后连哪一条边
发现如果连的边不是后边的第一个点,可以将后边的第一个点与后边的任意一个点交换来替代。
所以只会向后连一条边
当然,对于第零条边,肯定是联通的。
这样似乎就可以转移了
f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示为已经连了i条边,有j个强连通分量,已经用了k个点的方案数
则有:
f [ i + 1 ] [ p ] [ k ] = f [ i + 1 ] [ p ] [ k ] + f [ i ] [ j ] [ k ] f [ i + 1 ] [ n ] [ k ] = f [ i + 1 ] [ n ] [ k ] + f [ i ] [ j ] [ k ] f [ i + 1 ] [ j + 1 ] [ k + 1 ] = f [ i + 1 ] [ j + 1 ] [ k + 1 ] + f [ i ] [ j ] [ k ] f[i+1][p][k]=f[i+1][p][k]+f[i][j][k]\\ f[i+1][n][k]=f[i+1][n][k]+f[i][j][k]\\ f[i+1][j+1][k+1]=f[i+1][j+1][k+1]+f[i][j][k] f[i+1][p][k]=f[i+1][p][k]+f[i][j][k]f[i+1][n][k]=f[i+1][n][k]+f[i][j][k]f[i+1][j+1][k+1]=f[i+1][j+1][k+1]+f[i][j][k]
最后判定会不会出现重边,推一下发现如果 i > ( j − 1 ) ∗ ( j − 2 ) / 2 + ( k − j + 1 ) ∗ ( k − 1 ) i>(j-1)*(j-2)/2+(k-j+1)*(k-1) i>(j1)(j2)/2+(kj+1)(k1)的话,那么说明就有了重边。判掉就好了

代码

#include<cstdio>
#define mod 998244353
#define N 103
#define M 10003
using namespace std;
int n;
long long f[M][N][N];
int main()
{
	freopen("counting.in","r",stdin);
	freopen("counting.out","w",stdout);
	scanf("%d",&n);
	f[0][1][1]=1;
	for (int i=0;i<=n*(n-1);i++)
	{
		for (int j=0;j<=n;j++)
			for (int k=0;k<=n;k++)
			{
				int pn=j+n-k;
				if (i>(k-j+1)*(k-1)+(j-1)*(j-2)/2) f[i][j][k]=0;
			}
		int j,k;j=k=1;
		for (int j=0;j<=n;j++)
		{
			for (int k=0;k<=n;k++)
			{
				if (f[i][j][k]==0) continue;
				f[i+1][j+1][k+1]=(f[i+1][j+1][k+1]+f[i][j][k]);
				if (f[i+1][j+1][k+1]>mod) f[i+1][j+1][k+1]-=mod;
			//	f[i+1][j][k]=(f[i+1][j][k]+f[i][j][k])%mod;
				for (int p=1;p<j;p++)
				{
					f[i+1][p][k]=(f[i+1][p][k]+f[i][j][k]);
					if (f[i+1][p][k]>mod) f[i+1][p][k]-=mod;
				}
			}
			f[i+1][j][n]=(f[i+1][j][n]+f[i][j][n])%mod;
			if (f[i+1][j][n]>mod) f[i+1][j][n]-=mod;
		}
	}
	for (int i=1;i<=n*(n-1);i++)
	{
		long long ans=0;
		for (int j=1;j<=n;j++)
		{
			for (int k=0;k<=n;k++)
			{
				int pn=j+n-k;
				if (i<=(k-j+1)*(k-1)+(j-1)*(j-2)/2)
					ans=(ans+f[i][j][k])%mod;
			}
		}
		printf("%lld ",ans);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值