AT_arc139_d 做题记录
在北京联训期间做的这题,感谢 yhw 老师的帮助。挺困难的题(对我来说),我一个晚上才搞定它,所以尽量写得细致一点,让后人更快看懂。
题意
给定一个大小为 $n$ 的可重集合 $S$,值域为 $m$,必须进行 $k$ 次操作,每次随便选一个数 $1 \leq v \leq m$,把 $v$ 加入 $S$,再删除 $S$ 中第 $x$ 大的数。求最后所有方案中 $S$ 的总和的总和。范围在 $2000$ 之内。
题目分析
这一波操作下来,集合 $S$ 变成的样子已经很难看了,几乎很不可做。但你的目的是求和,所以可以进行如下变换,看好了:
$$\sum_{i=1}na_i=\sum_{i=1}n\sum_{j=1}{a_i}1=\sum_{j=1}cnt([j \leq a_i])$$
其实很好理解,相当于你把 $S$ 画成柱状图,你从下往上扫描,$cnt([j \leq a_i])$ 是每层的格子数,下面简记为 $cnt(j)$。
答案是这一切的总和,你可以枚举上式的 $j$,求这个 $j$ 的和,再加起来。
现在考虑 $cnt(j)$ 的变化。显然,如果这次选择的 $v \geq j$,$cnt(j)$ 加一,否则不变。然后删除第 $x$ 小的数。这个等价于如果 $cnt(j)>n-x+1$,$cnt(j)$ 减一,否则不变。
体会一下这个过程。你发现,如果起始的 $cnt(j) \geq n-x+1$,它在每次被加之后会一直减,直到它等于 $n-x+1$ 后,就被定住了;否则也差不多,你加加加加加,直到等于 $n-x+1$,也会被定住。你得到了最关键的性质:最终的 $cnt(j)$ 只与初始值和被加的次数相关。
最终做法
先计算出最开始的 $cnt(j)$,然后,枚举你加一的次数 $p$。按照刚才讲的算出最终状态,记为 $c$,柿子便很简单,$(m-j+1)p(j-1)\binom{k}{p}c$。
复杂度 $O(m(n+k))$。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=2001,M=998244353;
int n,m,k,x,ans,a[N],fac[N],inv[N];
int fpow(int x,int y)
{
int ans=1;
while(y)
{
if(y&1) ans=ans*x%M;
x=x*x%M;
y>>=1;
}
return ans;
}
int C(int n,int m)
{
return fac[n]*inv[m]%M*inv[n-m]%M;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>m>>k>>x;
for(int i=1;i<=n;i++) cin>>a[i];
fac[0]=fac[1]=inv[0]=inv[1]=1;
for(int i=2;i<=k;i++)
{
fac[i]=fac[i-1]*i%M;
inv[i]=(M-M/i)*inv[M%i]%M;
}
for(int i=2;i<=k;i++) inv[i]=inv[i]*inv[i-1]%M;
for(int i=1;i<=m;i++)
{
int c=0;
for(int j=1;j<=n;j++) if(a[j]>=i) c++;
for(int j=0;j<=k;j++)
{
int nc;
if(c>n-x+1) nc=max(c+j-k,n-x+1);
else nc=min(c+j,n-x+1);
ans=(ans+fpow(m-i+1,j)*fpow(i-1,k-j)%M*C(k,j)%M*nc)%M;
}
}
cout<<ans;
return 0;
}

浙公网安备 33010602011771号