[CSP校内集训]kat

题意

一本有n道题的练习册,katarina大佬每天都会做k道题。
第一天做第1~k题,第二天做第2~k+1题……第n-k+1天做第n−k+1~n道题。
每道题有它的难度值,假设今天katarina大佬做的题目中最大难度为t,那么今天katarina大佬的劳累度就是\(w_t\),做完这本书的劳累值就是每天的劳累值之和。
题目的难度在1~m随机,求做完整本书的期望劳累值

\(n,m \leq 500 , mod = 10^9+7\)

思路

由于前面两道题都有点迷所以还以为这道题也是道神题

性质:求每种排列的贡献和 等价于 一个滑动窗口的贡献\(\times\) \((n-k+1)\)

性感)证明

根据题意有:\[ans = \sum_{i=1}^{m^n}{ (\frac{1}{m^n} \times (a_1^i + a_2^i + ....... + a_{n-k+1}^i)) }\]其中\(a_j^i\)表示在第\(i\)种排列下第\(j\)个滑动窗口的贡献

显然这个东西可以用乘法分配律按照\(j\)分开:\[ans = \sum_{j=1}^{n-k+1}{(\frac{1}{m^n} \times (a_j^1 + a_j^2 + ....... + a_j^{m^n}))}\]

对于一个滑动窗口,在它范围外的其他格子不管怎么变都不会影响它的贡献,所以这样的\(a\)是相同的,每种贡献有\(m^{n-k}\)

把相同贡献的变成一个,最后变成:\[ans = \sum_{j=1}^{n-k+1}{ (\frac{1}{m^k} \times (a_j^1 + a_j^2 + ........ + a_j^{m^{n-k}}))) }\]

发现此时答案为每个滑动窗口单独的贡献加起来,而每个滑动窗口的贡献又相同,所以就等于单个贡献\(\times\) \((n-k+1)\)

对单个滑窗期望DP即可,这个复杂度可以是\(O(mlogn)\)或者\(O(nm)\),比较基础就不写了(不能理解题解\(O(n^2m)\)的睿智做法

本题难点不在期望DP而在简化问题(当然像题解一样直接莽就另当别论了),其实\(m\)可以开到\(10^5\)

Code

#include<bits/stdc++.h>
#define N 505
#define Min(x,y) ((x)<(y)?(x):(y))
#define Max(x,y) ((x)>(y)?(x):(y))
using namespace std;
typedef long long ll;
const ll mod = 1000000007;
int n,m,k;
ll a[N],f[N][N],sum[N][N];

template <class T>
void read(T &x)
{
    char c; int sign=1;
    while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
    while((c=getchar())>='0'&&c<='9') x=(x<<1)+(x<<3)+c-48; x*=sign;
}
ll quickpow(ll a,ll b)
{
    ll ret=1;
    while(b)
    {
        if(b&1) ret=ret*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ret;
}

int main()
{
    freopen("kat.in","r",stdin);
    freopen("kat.out","w",stdout);
    read(n);read(m);read(k);
    for(int i=1;i<=m;++i) read(a[i]);
    for(int i=1;i<=m;++i) f[1][i]=quickpow(m,mod-2),sum[1][i]=(sum[1][i-1]+f[1][i])%mod;
    for(int i=2;i<=k;++i)
    {
        for(int j=1;j<=m;++j)
        {
            f[i][j]=(sum[i-1][j-1]+f[i-1][j]*j%mod)*quickpow(m,mod-2)%mod;
            sum[i][j]=(sum[i][j-1]+f[i][j])%mod;
        }
    }
    ll ans=0;
    for(int i=1;i<=m;++i) ans=(ans + f[k][i]*a[i]%mod)%mod;
    cout<<(ans*(n-k+1)%mod+mod)%mod<<endl;
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Chtholly/p/11762707.html