【UOJ#340】【清華集訓2017】小 Y 和恐怖的奴隸主(矩陣快速冪,動態規劃)
阿新 • • 發佈:2019-01-05
【UOJ#340】【清華集訓2017】小 Y 和恐怖的奴隸主(矩陣快速冪,動態規劃)
題面
題解
考慮如何暴力\(dp\)。
設\(f[i][a][b][c]\)表示當前到了第\(i\)次攻擊,還剩下的\(1,2,3\)血的奴隸主個數為\(a,b,c\)的概率,每次考慮打到了哪裡,做一個轉移。
這樣子,狀態數就是把不超過\(8\)個東西分配到\(3\)個集合中,狀態有\(165\)種,再加一個狀態記錄糊臉上的期望,也就是\(166\)個狀態。
直接矩乘優化,那麼單次的複雜度就是\(O(166^3*log n)\),顯然過不了。
把\(2^k\)的矩陣預處理出來,每次拿行向量去乘矩陣,優化到\(O(166^2*log n)\)
然後就卡卡卡卡卡卡常吧。
提供幾個卡常方式:
- 陣列卡著資料範圍開
- 隨時交換迴圈順序
- 手動矩乘
#include<iostream> #include<cstdio> using namespace std; #define ll long long #define MOD 998244353 void add(int &x,int y){x+=y;if(x>=MOD)x-=MOD;} inline ll read() { ll x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } int m,K;ll n; int bh[9][9][9],tot,lim[5]; int inv[20]; int P[61][170][170]; int A[170],tmp[170],Ans[170]; void dfs(int a,int b,int c) { if(a>lim[1]||b>lim[2]||c>lim[3])return; if(bh[a][b][c]||a+b+c>K)return; bh[a][b][c]=++tot; dfs(a+1,b,c);dfs(a,b+1,c);dfs(a,b,c+1); } void Plus(int &A,int &B,int &C){if(m==1)++A;if(m==2)++B;if(m==3)++C;} void pre() { inv[0]=inv[1]=1;for(int i=2;i<20;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD; for(int a=0;a<=K&&a<=lim[1];++a) for(int b=0;a+b<=K&&b<=lim[2];++b) for(int c=0;a+b+c<=K&&c<=lim[3];++c) { int s=a+b+c+1,p=bh[a][b][c]; add(P[0][p][tot+1],inv[s]);add(P[0][p][p],inv[s]); if(a)P[0][p][bh[a-1][b][c]]=(P[0][p][bh[a-1][b][c]]+1ll*a*inv[s])%MOD; if(b) { int A=a+1,B=b-1,C=c;if(s<=K)Plus(A,B,C); P[0][p][bh[A][B][C]]=(P[0][p][bh[A][B][C]]+1ll*b*inv[s])%MOD; } if(c) { int A=a,B=b+1,C=c-1;if(s<=K)Plus(A,B,C); P[0][p][bh[A][B][C]]=(P[0][p][bh[A][B][C]]+1ll*c*inv[s])%MOD; } } add(P[0][tot+1][tot+1],1); A[bh[m==1][m==2][m==3]]=1; for(int p=1;p<=60;++p) for(int i=1;i<=tot+1;++i) for(int k=1;k<=tot+1;++k) for(int j=1;j<=tot+1;++j) P[p][i][j]=(P[p][i][j]+1ll*P[p-1][i][k]*P[p-1][k][j])%MOD; } int main() { int T=read();m=read();K=read(); for(int i=1;i<=m;++i)lim[i]=K; dfs(0,0,0);pre(); while(T--) { n=read();int p=0; for(int i=1;i<=tot+1;++i)Ans[i]=A[i]; while(n) { if(n&1) { for(int i=1;i<=tot+1;++i)tmp[i]=Ans[i],Ans[i]=0; for(int k=1;k<=tot+1;++k) for(int j=1;j<=tot+1;++j) Ans[j]=(Ans[j]+1ll*tmp[k]*P[p][k][j])%MOD; } ++p;n>>=1; } printf("%d\n",Ans[tot+1]); } return 0; }