洛谷P5472/LOJ3160[NOI2019]斗主地(DP+组合数学)

既然百度的题解都是拉格朗日插值,我来写一个初中数学水平的

首先看到各次洗牌是无关的,所以可以一次次来

然后对于第一堆有$a$个,选到正数第$i$个,第二堆有$b$个,选到第$j$个,我们记这种情况发生的概率为$E_{a,b,i,j}$,不难看出新的$a_i'=\sum\limits^A_{i=0}\sum\limits^{n-A-i}_{j=0}\frac{i\times a_i+j\times a_{A+j}}{i+j}\times E_{A,n-A,i,j}$(若$i\le 0,i>A,j\le 0,j>n-A$则对应项为$0$)

那么这个$E_{a,b,i,j}$怎么求呢?如果我们先选到第一堆的$i$个,再选到第二堆的$j$个,则概率为$\frac{a\times (a-1)\times(a-2)\times\cdots\times(i+1)\times b\times (b-1)\times (b-2)\times\cdots\times(j+1)}{(a+b)\times (a+b-1)\times(a+b-2)\times\cdots\times(i+j+1)}$;若先选到第二堆的$j$个,再选到第一堆的$i$个,则概率为$\frac{b\times (b-1)\times (b-2)\times\cdots\times(j+1)\times a\times (a-1)\times(a-2)\times\cdots\times(i+1)}{(a+b)\times (a+b-1)\times(a+b-2)\times\cdots\times(i+j+1)}$。

它们是一样的!

对于其它情况,易知概率仍是一样的,均为$\frac{\frac{a!}{i!}\times\frac{b!}{j!}}{\frac{(a+b)!}{(i+j)!}}=\frac{a!\times b!\times (i+j)!}{(a+b)!\times i!\times j!}$,而很容易看出情况有$C^{a-i}_{a-i+b-j}=\frac{(a-i+b-j)!}{(a-i)!\times (b-j)!}$种,用人尽皆知的$O(n+log998244353)$方法求出$i^{-1}$,$i!$和$(i!)^{-1}(mod998244353)$就可以$O(1)$求$E_{a,b,i,j}$,套上$dp$就是$O(mn^2)$的,拿到$30pts$,加上矩阵加速即可以拿到$40pts$。

$100pts$怎么拿呢?让我们观察样例里4个位置的期望分数:$\frac{7}{4},\frac{9}{4},\frac{11}{4},\frac{13}{4}$

它们是等差数列!

于是我们大胆猜想:$type=2$时答案是二阶等差数列,打表可证这个结论是正确的

对于一阶情况的不完整证明:对于每步确定的$A$,从$a$到$a'$的转移是完全确定的,也就是说每步$a_i'$必定可以表示为$\sum\limits^n_{j=1}c_{i,j}a_j$的形式($c_{i,j}$是常数且$\sum\limits^n_{j=1}c_{i,j}=1$),考虑设$a_i=a+(i-1)d$,则有$a_i'=\sum\limits^n_{j=1}c_{i,j}(a+(j-1)d)=a-d+\sum\limits^n_{j=1}c_{i,j}j$,那么所有情况都可以化归到1~n的情况,对这一情况归纳即可。

对于二阶同理。

由于等差数列可以看作特殊的二阶等差数列,所以只推出前3项就可以求出整个数列了,复杂度下降到$O(m)$,可以AC

可怎么求呢?dalao们都用拉格朗日插值,本蒟蒻提出一个小学方法:

设$d=a_2-a_1,d'=(a_3-a_2)-(a_2-a_1)=a_3+a_1-2a_2$,则根据$d'$为数列的二阶公差,则$a_2=a_1+d$,$a_3=a_1+d+(d+d')$,$a_4=a_1+d+(d+d')+(d+2d')$,……,$a_i=a_1+(i-1)d+[1+2+\cdots+(i-2)]d'=a_1+(i-1)d+\frac{(i-1)(i-2)}{2}d'$。

那么就可以愉快地上代码了~

#include<cstdio>
typedef long long ll;
const int mod=998244353;
const int N=10000050;
char rB[1<<21],*S,*T,wB[1<<21];
int wp=-1;
inline char gc(){return S==T&&(T=(S=rB)+fread(rB,1,1<<21,stdin),S==T)?EOF:*S++;}
inline void flush(){fwrite(wB,1,wp+1,stdout);wp=-1;}
inline void pc(char c){if(wp+1==(1<<21))flush();wB[++wp]=c;}
inline int rd(){
    char c=gc();
    while(c<48||c>57)c=gc();
    int x=c&15;
    for(c=gc();c>=48&&c<=57;c=gc())x=(x<<3)+(x<<1)+(c&15);
    return x;
}
short buf[15];
inline void wt(int x){
    short l=-1;
    while(x>9){
        buf[++l]=x%10;
        x/=10;
    }
    pc(x|48);
    while(l>=0)pc(buf[l--]|48);
    pc('\n');
}
int a[5],b[5],c[5],f[N],g[N],v[N],n;
void exgcd(int a,int b,int &x,int &y){
    if(!b){x=1;y=0;}
    else{
        exgcd(b,a%b,y,x);
        y-=a/b*x;
    }
}
inline int inv(int a){
    int x,y;
    exgcd(a,mod,x,y);
    return x<0?x+mod:x;
}
inline int E(int a,int b,int p,int q){  //算E
    return (ll)g[n]*f[p+q]%mod*f[a]%mod*g[p]%mod*f[b]%mod*g[q]%mod*f[n-p-q]%mod*g[a-p]%mod*g[b-q]%mod;
}
inline int val(int p){  //根据a1,a2,a3算ap
    if(p>n)return 0;
    int d=(a[2]-a[1]+mod)%mod,dd=((ll)a[3]+a[1]-(a[2]<<1)+(mod<<1))%mod;
    return (a[1]+(ll)d*(p-1)+(ll)dd*(p-1)%mod*(p-2)%mod*499122177)%mod;  //499122177是2的逆元
}
int main(){
    int m,q,type,i,j,A;
    n=rd();m=rd();type=rd();
    f[0]=g[0]=1;
    for(i=1;i<=n;++i)f[i]=(ll)f[i-1]*i%mod;
    v[n]=(ll)(g[n]=inv(f[n]))*f[n-1];
    for(i=n-1;i;--i)v[i]=(ll)(g[i]=(ll)g[i+1]*(i+1)%mod)*f[i-1];
    if(type==1){a[1]=1;a[2]=2;a[3]=3;}
    else{a[1]=1;a[2]=4;a[3]=9;}
    while(m--){
        A=rd();
        b[1]=a[1];b[2]=A>=2?a[2]:0;b[3]=A>=3?a[3]:0;
        c[1]=val(1+A);c[2]=val(2+A);c[3]=val(3+A);
//把a要用到的值分两堆存下来,a就可以被覆盖掉了
        a[1]=a[2]=a[3]=0;
        for(i=0;i<=3&&i<=A;++i)
            for(j=0;j<=3-i&&j<=n-A;++j)if(i+j)a[i+j]=(a[i+j]+((ll)i*b[i]+(ll)j*c[j])%mod*v[i+j]%mod*E(A,n-A,i,j))%mod;
//这里越界的b[0]和c[0]都是0,排除了情况i+j==0时没必要算
    }
    q=rd();
    while(q--){
        i=rd();
        wt(val(i));
    }
    flush();
    return 0;
}
View Code

 

posted @ 2019-07-27 16:35  wangyuchen  阅读(396)  评论(0编辑  收藏  举报