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

Luogu P6633 [ZJOI2020] 抽卡

时间:2020-07-29 12:41:07      阅读:63      评论:0      收藏:0      [点我收藏+]

标签:pow   编号   printf   turn   lang   表示   存在   就是   for   

其实我只是来写一发暴力70pts的DP的说,正解拉格朗日反演牛顿迭代什么的根本策不懂

恭喜彩笔hl666再次因为快速幂忘记返回值调了快一个小时

这种关于轮次的求期望类似于[ZJOI2019]麻将的方法,考虑第\(i\)轮对答案的贡献就是前\(i\)轮操作之后都到不了终止状态的概率(集合\(End\)表示存在\(k\)个连续的数)

\[ans=\sum_{S\not \in End} \sum_{i\ge 0} P(\text{经过$i$轮后选出的集合恰好为$S$}) \]

我们显然可以容斥掉恰好为\(S\)的情况,即(其中\(ext_j\)表示是否存在编号为\(j\)的卡):

\[P(\text{经过$i$轮后选出的集合恰好为$S$})=\sum_{T\subset S} (-1)^{|S|-|T|} (\frac{\sum_{j\in T} ext_j}{m})^i \]

带回原式子就有:

\[ans=\sum_{S\not \in End} \sum_{i\ge 0} \sum_{T\subset S} (-1)^{|S|-|T|} (\frac{\sum_{j\in T} ext_j}{m})^i\=\sum_{S\not \in End} \sum_{T\subset S} (-1)^{|S|-|T|} \frac{m}{m-\sum_{j\in T} ext_j} \]

我们考虑枚举\(\sum_{j\in T} ext_j\),然后构造生成函数

\(w_i(x)=x^{ext_i}-1,G(x)=\sum_{S\not \in End}\prod_{i\in S} w_i(x)\),则答案为\(\sum_{i=0}^{m-1} \frac{m}{m-i}[x^i]G(x)\)

我们发现当\(ext_i=0\)\(w_i(x)=0\),否则\(w_i(x)=x-1\),因此我们只需要考虑选\(ext_i=1\)的点即可

设一个DP\(f_{i,j}\)表示前\(i\)种编号选出\(j\)\(ext_i=1\)的卡,且满足这些卡不存在连续\(k\)个的方案数,转移通过容斥显然是\(O(1)\)

那么现在\(G(x)=\sum_{i\ge 0} f_{n,i}\times (x-1)^i\),考虑\(O(n^2)\)求出\(f_n\)之后用二项式定理展开统计即可

总体复杂度\(O(n^2)\)(设\(n,m\)同阶)

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=5005,mod=998244353;
int n,m,k,x,f[N<<1][N],sz[N<<1],ans,fact[N<<1],inv[N<<1];
inline void inc(int& x,CI y)
{
	if ((x+=y)>=mod) x-=mod;
}
inline void dec(int& x,CI y)
{
	if ((x-=y)<0) x+=mod;
}
inline int quick_pow(int x,int p=mod-2,int mul=1)
{
	for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
inline void init(CI n)
{
	RI i; for (fact[0]=i=1;i<=n;++i) fact[i]=1LL*fact[i-1]*i%mod;
	for (inv[n]=quick_pow(fact[n]),i=n-1;~i;--i) inv[i]=1LL*inv[i+1]*(i+1)%mod;
}
inline int C(CI n,CI m)
{
	return 1LL*fact[n]*inv[m]%mod*inv[n-m]%mod;
}
int main()
{
	RI i,j; for (scanf("%d%d",&m,&k),i=1;i<=m;++i)
	scanf("%d",&x),++sz[x],n=max(n,x);
	for (f[0][0]=i=1;i<=n;++i)
	{
		for (j=0;j<=sz[i-1];++j) f[i][j]=f[i-1][j];
		if (!sz[i]) sz[i]+=sz[i-1]; else
		{
			for (sz[i]+=sz[i-1],j=1;j<=sz[i];++j)
			{
				inc(f[i][j],f[i-1][j-1]);
				if (j>=k&&sz[i]-sz[i-k]==k)
				dec(f[i][j],f[i>k?i-k-1:0][j-k]);
			}
		}
	}
	for (init(n),i=0;i<m;++i)
	{
		int ret=0; for (j=i;j<=n;++j)
		if ((j-i)&1) dec(ret,1LL*C(j,i)*f[n][j]%mod);
		else inc(ret,1LL*C(j,i)*f[n][j]%mod);
		inc(ans,1LL*m*quick_pow(m-i)%mod*ret%mod);
	}
	return printf("%d",ans),0;
}

Luogu P6633 [ZJOI2020] 抽卡

标签:pow   编号   printf   turn   lang   表示   存在   就是   for   

原文地址:https://www.cnblogs.com/cjjsb/p/13396040.html

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