NOI 2019 題目選做
阿新 • • 發佈:2022-05-08
鬥主地
題目描述
解法
首先考慮 \(30\) 分的做法,我們可以設計 \(f[i][j]\) 表示前 \(i\) 輪第 \(j\) 個位置的期望分數,\(g[i][j]\) 表示對於現在這一輪的 \(a\),第一堆取走了 \(i\) 個,第二堆取走了 \(j\) 個的概率,轉移很容易寫。
結論是:一次函式洗牌之後的期望仍然是一次函式,二次函式洗牌後的期望仍然是二次函式。由於我的數學功底太差,所以並不能證明這個結論。知道這個結論以後我們用 \(dp\) 維護前三項然後插值即可,時間複雜度 \(O(n+9\cdot m)\)
#include <cstdio> #include <cstring> const int M = 105; #define int long long const int MOD = 998244353; int read() { int x=0,f=1;char c; while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;} while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();} return x*f; } int n,m,q,w,tp,A,B,C,f[5],l[5],r[5],g[5][5],inv[10000005]; void add(int &x,int y) {x=(x+y)%MOD;} int qkpow(int a,int b) { int r=1; while(b>0) { if(b&1) r=r*a%MOD; a=a*a%MOD; b>>=1; } return r; } int ask(int x) { return (A*x%MOD*x+B*x+C)%MOD; } signed main() { n=read();m=read();tp=read(); if(tp==1) A=0,B=1,C=0; if(tp==2) A=1,B=0,C=0; inv[0]=inv[1]=1; for(int i=2;i<=n;i++) inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD; for(int i=1;i<=m;i++) { int x=read(),y=n-x;w^=1; for(int j=1;j<=3;j++) l[j]=ask(j),r[j]=ask(j+x); memset(f,0,sizeof f); memset(g,0,sizeof g);g[0][0]=1; for(int j=0;j<=x && j<=3;j++) for(int k=0;k<=y && k<=3;k++) { int v=inv[x-j+y-k]; if(j<x) add(g[j+1][k],g[j][k]*v%MOD*(x-j)); if(k<y) add(g[j][k+1],g[j][k]*v%MOD*(y-k)); } for(int j=1;j<=n && j<=3;j++) for(int k=1;k<=j;k++) { int v=inv[n-j+1]; if(k<=x) add(f[j],l[k]*g[k-1][j-k]%MOD*(x-k+1)%MOD*v); if(k<=y) add(f[j],r[k]*g[j-k][k-1]%MOD*(y-k+1)%MOD*v); } A=((f[3]-2*f[2]+f[1])*inv[2]%MOD+MOD)%MOD; B=((8*f[2]-5*f[1]-3*f[3])*inv[2]%MOD+MOD)%MOD; C=((3*f[1]-3*f[2]+f[3])%MOD+MOD)%MOD; } q=read(); while(q--) printf("%lld\n",ask(read())); }