#NOIP模拟赛bzoj3449#大佬(期望好题)

来源:互联网 发布:网络博客犯法吗 编辑:程序博客网 时间:2024/06/06 02:48




其实我根本就不懂得期望是什么,准确地说应该是不太熟悉,但是现在大致也知道是怎么算的了。


说说这道题,出题人的正解很成功被我们某些大佬给卡掉了,原因是还有更好的解法,可以把时间复杂度降得更低,现在来看真正的正解。


假设现在我们有一种完整的N道题难度的排列,设现在这种排列为i,然后有w[i][j]来表示这种排列下,第j - k + 1天到第j天的劳累值。

对于方案i,完成总书的劳累值就是Σw[i][j](k <= j <= n)

然后一共有M^N种排列, 即i∈[1, M^N]

那么列式如下:


上面的竖着一列求和就是在排列i下的总劳累值。

我们考虑每一行,在每种情况下,位置k的劳累值与前后的并不相关,只与它内部自己的值有关,也就是说在k这个位置上所有方案的劳累值总和能直接算出来

然后对于每一个区间大小为K的位置(即每一行求和),它们自己的劳累值总和其实是一样的。

只需要算出一行,然后乘N- K+ 1就可以了。

一行怎么算?

枚举最大劳累值j,于是这个长度为K的区间里要填至少一个j,其他的都要小于j,

于是此时有:(j ^ K - (j - 1) ^ K) * Wt[j]就是当前j的贡献,而这种贡献将出现M ^ (N - K)次(其他地方任意排列)。


对于总共的期望,一共有M ^ N种情况,而每种情况的贡献已经累和,可以选择最后来除,也可以算出每一种最大值就马上除(约分一下就是除M ^ K, 即乘逆元)


Code:

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>using namespace std;typedef long long LL;const int Max = 400;const int MOD = 1e9 + 7;int N, M, K;int Wt[Max + 5];void getint(int & num){    char c; int flg = 1;    num = 0;    while((c = getchar()) < '0' || c > '9') if(c == '-')    flg = -1;    while(c >= '0' && c <= '9'){    num = num * 10 + c - 48;    c= getchar(); }    num *= flg;}int qmul(int p, int k){    int rt = 1;    while(k){        if(k & 1)   rt = 1ll * rt * p % MOD;        p = 1ll * p * p % MOD;        k >>= 1;    }    return rt;}int main(){    //freopen("kat.in", "r", stdin);    //freopen("kat.out", "w", stdout);    getint(N), getint(M), getint(K);    //if(N < K){puts("0");    return 0;}    for(int i = 1; i <= M; ++ i)    getint(Wt[i]);    int Ans = 0ll;    int deno = qmul(M, K);    deno = qmul(deno, MOD - 2);    for(int i = 1; i <= M; ++ i)        Ans = (Ans + 1ll*Wt[i]*(qmul(i, K)-qmul(i - 1, K))%MOD*deno%MOD)%MOD;    Ans = 1ll*Ans * (N - K + 1) % MOD;    printf("%d\n", Ans);    return 0;}/*2 2 21 2*/


原创粉丝点击