P4707 重返现世
做的第一道 \(\min-\max\) 容斥题。
题目相当于求所有集合第 \(E(min_k S)\) 其中 \(min_k S\) 表示 \(S\) 中物品出现时间的第 \(k\) 小。
这种形式跟拓展 \(\min-\max\) 容斥的形式完全一致。
\(E(\max T)\) 为集合内物品出现时间的最大值的期望,无法简单的表示。而 \(E(\min T)\) 为集合内物品出现时间的最小值的期望,等价于第一次出现 \(T\) 中物品的期望时间,即为 \(\frac{m}{\sum_{i\in T}p_i}\)。
将 \(min_k\) 转化为 \(\max_{n-k+1}\) 所以问题等价于计数:
于是设计状态 \(f_{i,j,l}\) 表示前 \(i\),\(\sum p_i=j\),\(|T|=l\) 的方案数。
最后容斥即可。
UesugiErii
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
#define fin(x) freopen(#x".in","r",stdin)
#define fout(x) freopen(#x".out","w",stdout)
#define fr(x) fin(x),fout(x);
#define Fr(x,y) fin(x),fout(y)
#define INPUT(_1,_2,FILE,...) FILE
#define IO(...) INPUT(__VA_ARGS__,Fr,fr)(__VA_ARGS__)
using namespace std;
using namespace __gnu_pbds;
#define mp make_pair
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define cfast ios::sync_with_stdio(false);cin.tie(0),cout.tie(0)
#define ll long long
#define ull unsigned long long
#define intz(x,y) memset((x),(y),sizeof((x)))
char *p1,*p2,buf[100000];
#define nc() (p1==p2 && (p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
#define tup(x) array<int,(x)>
inline ll read(){
ll x=0,f=1;char ch=nc();
while(ch<48||ch>57){if(ch=='-')f=-1;ch=nc();}
while(ch>=48&&ch<=57)x=x*10+ch-48,ch=nc();
return x*f;
}
//void write(int x){cout<<x<<' ';}
//void write(pii x){cout<<"P("<<x.fi<<','<<x.se<<")\n";}
//void write(vector<auto>x){for(auto i:x)write(i);cout<<'\n';}
//void write(auto *a,int l,int r){for(int i=l;i<=r;i++)write(a[i]);cout<<'\n';}
inline ll lowbit(ll x){return x&-x;}
#define pcount(x) __builtin_popcount(x)
inline void cmx(auto &x,ll y){if(y>x)x=y;}
inline void cmn(auto &x,ll y){if(y<x)x=y;}
inline int max(vector<int>w){int res=-1e9;for(int i:w)cmx(res,i);return res;}
const int mod=998244353;
ll qp(ll x,int y){ll res=1;for(;y;x=x*x%mod,y>>=1)if(y&1)res=res*x%mod;return res;}
const int N=1005,M=1e4+5;
int p[N],f[M][N],ifac[N],fac[N];
int C(int x,int y){return fac[x]*ifac[y]%mod*ifac[x-y]%mod;}
inline void UesugiErii(){
int n,m,k,ans=0;cin>>n>>k>>m,k=n-k+1;
for(int i=1;i<=n;i++)cin>>p[i];
for(int i=fac[0]=1;i<=n;i++)fac[i]=fac[i-1]*i%mod;
ifac[n]=qp(fac[n],mod-2);
for(int i=n;i;i--)ifac[i-1]=ifac[i]*i%mod;
f[0][0]=1;
for(int i=1;i<=n;i++){
int t=i&1;
for(int j=m;~j;j--)
for(int l=n;~l;l--)
if(j>=p[i]&&l)
f[j][l]=(f[j][l]+f[j-p[i]][l-1])%mod;
else f[j][l]=f[j][l];
}
for(int i=1;i<=m;i++)
for(int j=k;j<=n;j++)
ans=(ans+((j-k)&1?-1:1)*C(j-1,k-1)*m%mod*qp(i,mod-2)%mod*f[i][j]%mod)%mod;
cout<<(ans+mod)%mod;
}
signed main(){
//IO();//cfast;
int _=1;//cin>>_;
for(;_;_--)UesugiErii();
return 0;
}
但是这样虽然转移 \(O(1)\) 但状态数就已经是 \(O(n^2m)\) 级别的。滚掉一维能有 70 pts。
观察题目发现数据范围有 \(n-k\le 10\),等价于转化成 \(max_k\) 后 \(k\le 11\)。所以优化必定从这方面入手。发现 \(|T|\) 跟 \(k\) 都是一起出现的,考虑能否通过 \(k\) 刻画 \(|T|\) 的变化。
考虑选入一个物品时 \((-1)^{|T|-k}{|T|-1\choose k-1}\) 的变化。
\((-1)^{|T|-k}{|T|-1\choose k-1}\gets (-1)^{|T|-k+1}{|T|\choose k-1}\) 前一项就相当于取反,后一项有组合恒等式 \({|T|\choose k-1}={|T|-1\choose k-1}+{|T|-1\choose k-2}\),也就是多了后一项。
所以需要记录 \(f_{i,j,l}\) 表示前 \(i\) 个物品,\(\sum p=j\),\(k=l\) 时的 \(\sum_{T\subseteq S,|T|\ge k}(-1)^{|T|-k}{|T|-1\choose k-1}\sum_{i\in T}p_i\)。
转移式即为:
最后 \(ans=\sum_{i=1}^m f_{n,i,k}\frac{m}{i}\)。
这样状态数就是 \(O(nm)\),转移 \(O(1)\),可以滚掉第一维。
还有问题是怎么初始化。题解里的初始化也太人类智慧了。不过经过我的思考发现了一种很自然的初始化 /dy。
这个状态在 \(|T|=0\) 时无法初始化因为没有定义 \({x\choose y},x=-1\) 时的情况。
也就是当前 \(|T|=1\),等价于 \(f_{i,p_i,l}\) 从 \(f_{i-1,0,?}\),因为 \(f_{i-1,0,?}\) 没有定义,所以需要特殊转移:
当 \(l-1>0\) 时 \({0\choose l-1}=0\),所以等价于:
也就是每轮 \(f_{i,p_i,1}\gets f_{i,p_i,1}+1\) 即可。
UesugiErii
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
#define fin(x) freopen(#x".in","r",stdin)
#define fout(x) freopen(#x".out","w",stdout)
#define fr(x) fin(x),fout(x);
#define Fr(x,y) fin(x),fout(y)
#define INPUT(_1,_2,FILE,...) FILE
#define IO(...) INPUT(__VA_ARGS__,Fr,fr)(__VA_ARGS__)
using namespace std;
using namespace __gnu_pbds;
#define mp make_pair
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define cfast ios::sync_with_stdio(false);cin.tie(0),cout.tie(0)
#define ll long long
#define ull unsigned long long
#define intz(x,y) memset((x),(y),sizeof((x)))
char *p1,*p2,buf[100000];
#define nc() (p1==p2 && (p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
#define tup(x) array<int,(x)>
inline ll read(){
ll x=0,f=1;char ch=nc();
while(ch<48||ch>57){if(ch=='-')f=-1;ch=nc();}
while(ch>=48&&ch<=57)x=x*10+ch-48,ch=nc();
return x*f;
}
//void write(int x){cout<<x<<' ';}
//void write(pii x){cout<<"P("<<x.fi<<','<<x.se<<")\n";}
//void write(vector<auto>x){for(auto i:x)write(i);cout<<'\n';}
//void write(auto *a,int l,int r){for(int i=l;i<=r;i++)write(a[i]);cout<<'\n';}
inline ll lowbit(ll x){return x&-x;}
#define pcount(x) __builtin_popcount(x)
inline void cmx(auto &x,ll y){if(y>x)x=y;}
inline void cmn(auto &x,ll y){if(y<x)x=y;}
inline int max(vector<int>w){int res=-1e9;for(int i:w)cmx(res,i);return res;}
const int mod=998244353;
ll qp(ll x,int y){ll res=1;for(;y;x=x*x%mod,y>>=1)if(y&1)res=res*x%mod;return res;}
const int N=1005,M=1e4+5;
int p[N],f[2][12][M];//调换了后两维
inline void UesugiErii(){
int n,m,k,ans=0;cin>>n>>k>>m,k=n-k+1;
for(int i=1;i<=n;i++)cin>>p[i];
for(int i=1;i<=n;i++){
int t=i&1;
for(int j=0;j<=m;j++)
for(int l=1;l<=k;l++)f[t][l][j]=0;
for(int j=m;~j;j--)
for(int l=k;l;l--)
if(j>p[i])
(f[t][l][j]=f[!t][l][j]+(f[!t][l-1][j-p[i]]-f[!t][l][j-p[i]]+mod)%mod)%=mod;
else f[t][l][j]=f[!t][l][j];
(f[t][1][p[i]]+=1)%=mod;
}
for(int i=1;i<=m;i++)
ans=(ans+1ll*f[n&1][k][i]*m%mod*qp(i,mod-2)%mod)%mod;
cout<<ans;
}
signed main(){
//IO();//cfast;
int _=1;//cin>>_;
for(;_;_--)UesugiErii();
return 0;
}

浙公网安备 33010602011771号