[luogu P4707] 重返现世

\(\text{Problem}:\)重返现世

\(\text{Solution}:\)

\(t_{i}\) 表示原料 \(i\) 第一次被生成的期望时间,\(\max_{k}(S)\) 表示集合 \(S\) 中第 \(k\) 大的 \(t_{i}\)\(\min_{k}(S)\) 表示集合 \(S\) 中第 \(k\) 小的 \(t_{i}\)(当 \(k=1\) 时忽略不写)。那么题目要求的是 \(\min_{k}(S)\),其中 \(S\)\(n\) 种原料的全集。

对于本题给定集合 \(S\) 时,\(\max(S)\) 难以直接求出,而 \(\min(S)=\dfrac{m}{\sum_{i\in S}p_{i}}\),故考虑 \(\min-\max\) 容斥。

引理 \(1\)(扩展 \(\min-\max\) 容斥):

\[\begin{aligned} \max_k(S)&=\sum\limits_{T\subseteq S}(-1)^{\lvert T\rvert-k}\binom{\lvert T\rvert-1}{k-1}\min(T)\\ \min_{k}(S)&=\sum\limits_{T\subseteq S}(-1)^{\lvert T\rvert-k}\binom{\lvert T\rvert-1}{k-1}\max(T) \end{aligned} \]

其中 \(\max\)\(\min\) 对期望也成立。具体证明参考 OI-Wiki(容斥原理)

\(k=n-k+1\)。那么要求的是 \(\max_{k}(S)\),且 \(k\leq 11\)。枚举子集 \(T\) 可以做到 \(O(2^{n})\),无法通过。

考虑当集合 \(T\) 大小固定时,容斥系数也固定,那么答案为:

\[\max_{k}(S)=\sum\limits_{i=1}^{n}(-1)^{i-k}\binom{i-1}{k-1}\sum\limits_{T\subseteq S,\lvert T\rvert=i}\min(T) \]

当集合 \(T\)\(\sum p\) 确定时,可以直接求出 \(\min(T)\),故考虑类似背包的 \(dp\)。记 \(f_{i,j,k}\) 表示对于前 \(i\) 个数,\(\lvert T\rvert=j\)\(\sum p=k\) 的集合个数,有:

\[f_{i,j,k}=f_{i-1,j,k}+f_{i-1,j-1,k-p_{i}} \]

时间复杂度 \(O(n^2m)\),可以得到 \(70\) 分。

发现 \(dp\) 的转移难以优化,需要从状态定义下手。考虑答案的另一种表达形式:

\[\max_{k}(S)=\sum\limits_{i=1}^{m}\cfrac{m}{i}\sum\limits_{T\subseteq S,\sum_{j\in T}p_{j}=i}(-1)^{\lvert T\rvert-k}\binom{\lvert T\rvert-1}{k-1} \]

\(f_{i,j}\) 表示对于前 \(i\) 个数,\(\sum p=j\) 的系数之和。我们想要找到一种转移方式,使得计算系数与 \(\lvert T\rvert\) 无关(即不用计算组合数)。考虑从 \(f_{i-1,j-p_{i}}\rightarrow f_{i,j}\) 时,系数的转移方式:

\[(-1)^{\lvert T\rvert-k}\binom{\lvert T\rvert-1}{k-1}\rightarrow(-1)^{\lvert T\rvert+1-k}\binom{\lvert T\rvert}{k-1}=(-1)^{\lvert T\rvert +1-k}\left(\binom{\lvert T\rvert-1}{k-1}+\binom{\lvert T\rvert-1}{k-2}\right) \]

\(f_{i,j,k}\) 表示对于前 \(i\) 个数,\(\sum p=j\),当前求的是 \(\max_{k}(S)\),则转移变为:

\[f_{i,j,k}=f_{i-1,j,k}-f_{i-1,j-p_{i},k}+f_{i-1,j-p_{i},k-1} \]

边界条件为 \(f_{0,0,0}=1\)(感性理解),时间复杂度 \(O(nmk)\),滚动数组优化后空间复杂度 \(O(mk)\),可以通过。

\(\text{Code}:\)

#include <bits/stdc++.h>
#pragma GCC optimize(3)
#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
#define vi vector<int>
#define vpi vector<pair<int,int>>
using namespace std; const int N=10010, Mod=998244353;
inline int read()
{
	int s=0, w=1; ri char ch=getchar();
	while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); }
	while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar();
	return s*w;
}
int n,K,m,F[N][12],p[N];
inline int ksc(int x,int p) { int res=1; for(;p;p>>=1, x=x*x%Mod) if(p&1) res=res*x%Mod; return res; }
signed main()
{
	n=read(), K=read(), m=read();
	K=n-K+1;
	for(ri int i=1;i<=n;i++) p[i]=read();
	F[0][0]=1;
	for(ri int i=1;i<=n;i++)
	{
		for(ri int j=m;j>=p[i];j--)
		{
			for(ri int k=K;k;k--)
			{
				F[j][k]=(F[j][k]-F[j-p[i]][k]+F[j-p[i]][k-1]+Mod)%Mod;
			}
		}
	}
	int ans=0;
	for(ri int i=1;i<=m;i++) ans=(ans+F[i][K]*ksc(i,Mod-2))%Mod;
	printf("%lld\n",ans*m%Mod);
	return 0;
}
posted @ 2021-04-15 21:04  zkdxl  阅读(56)  评论(0编辑  收藏  举报