洛谷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; }