[省選集訓2022] 模擬賽13
未來
題目描述
給你一個長度為 \(n\) 的環狀陣列,每個位置有三種顏色:紅、綠、藍。
有 \(m\) 次操作,每次操作形如:對於所有位置 \(x\),如果位置 \(x\) 和位置 \(x+1\bmod n\) 顏色相同,那麼位置 \(x\) 顏色不變;否則位置 \(x\) 變為這兩個位置上沒有出現過的顏色。注意變化是同時進行的。
問操作 \(m\) 次之後得到的顏色陣列是什麼?
\(2\leq n\leq 5\cdot 10^5,0\leq m\leq 10^{18}\)
解法
考慮把顏色分別分別改寫成 \(0,1,2\),不難發現操作等價於 \(a_i'\leftarrow -a_i-a_{i+1\bmod n}\bmod 3\)
所以顯然的思路是多項式倍增,把 \((-1-x^{n-1})^m\bmod (x^n-1)\) 這東西算出來,然後和初始陣列卷積即可(本質上就是算貢獻,多項式是初始狀態和最終狀態之間的橋樑)
這樣已經能獲得大部分的分數,進一步的優化就考慮 \((-1-x^{n-1})^{3^k}\bmod 3=-1-x^{-3^k}\),這是因為,根據 \(\tt lucas\) 定理,其他的項都是 \(3\) 的倍數會被模掉,那麼我們把 \(m\) 拆位之後分別做即可,時間複雜度 \(O(n\log m)\)
其實跟這題是一樣的,沒什麼意思。
#pragma GCC optimize("Ofast") #include <cstdio> #include <iostream> using namespace std; const int M = 500005; #define int long long 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,a[M],b[M];char s[M]; signed main() { freopen("future.in","r",stdin); freopen("future.out","w",stdout); n=read();m=read();scanf("%s",s); for(int i=0;i<n;i++) a[i]=(s[i]=='r')?0:((s[i]=='g')?1:2); while(m) { int s=1;while(s<=m/3) s*=3; while(m>=s) { for(int i=0;i<n;i++) b[i]=2*(a[i]+a[(i+s)%n])%3; for(int i=0;i<n;i++) a[i]=b[i]; m-=s; } } for(int i=0;i<n;i++) putchar("rgb"[a[i]%3]); }
回憶
題目描述
\(n\times m\) 大小的矩陣,每個位置有值 \(a_{i,j}\),表示這個格子可能開放的概率,求最大四聯通塊的期望,模 \(998244353\)
\(n\times m\leq 40\)
解法
使用連通性狀壓的時候要勇敢一點,僅此而已。
假設 \(n<m\),我們考慮逐列遞推,發現需要記錄的狀態只有:連通性的最小表示法,每個位置的連通塊大小,歷史最大連通塊
這些狀態,那麼把它們打包放進 \(\tt vector\) 中,然後套上 \(\tt map\) 轉移即可。
#include <cstdio> #include <vector> #include <iostream> #include <map> using namespace std; const int M = 45; const int MOD = 998244353; #define x first #define y second #define vii vector<int> #define node pair<vii,vii> 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,ans,fa[10],a[M][M],b[M][M];map<node,int> f[2]; void add(int &x,int y) {x=(x+y)%MOD;} int find(int x) { while(x!=fa[x]) x=fa[x]; return x; } node operator + (node u,int s) { int cnt=0,use[10]={}; for(int i=0;i<10;i++) fa[i]=i; node r;r.x.resize(n); for(int i=0;i<n;i++) if(s>>i&1) { if(s>>i>>1&1) fa[find(i+1)]=find(i); if(u.x[i]) { for(int j=i+1;j<n;j++) if((s>>j&1) && u.x[i]==u.x[j]) fa[find(j)]=find(i); } } for(int i=0;i<n;i++) if((s>>i&1) && find(i)==i) r.x[i]=++cnt; for(int i=0;i<n;i++) if(s>>i&1) r.x[i]=r.x[find(i)]; r.y.resize(cnt+1); for(int i=0;i<n;i++) if(s>>i&1) { if(u.x[i] && !use[u.x[i]]) r.y[r.x[i]]+=u.y[u.x[i]],use[u.x[i]]=1; r.y[r.x[i]]++; } r.y[0]=u.y[0]; for(int i=1;i<=cnt;i++) r.y[0]=max(r.y[0],r.y[i]); return r; } signed main() { freopen("memory.in","r",stdin); freopen("memory.out","w",stdout); n=read();m=read(); for(int i=0;i<n;i++) for(int j=0;j<m;j++) a[i][j]=read(); if(n>m)//flip and reverse { for(int i=0;i<n;i++) for(int j=0;j<m;j++) b[i][j]=a[i][j],a[i][j]=0; for(int i=0;i<m;i++) for(int j=0;j<n;j++) a[i][j]=b[j][i]; swap(n,m); } node u;u.x.resize(n);u.y.resize(1); int w=0;f[0][u]=1; for(int i=0;i<m;i++) { w^=1;f[w].clear(); for(int s=0;s<(1<<n);s++) { int p=1; for(int j=0;j<n;j++) { if(s>>j&1) p=1ll*p*a[j][i]%MOD; else p=1ll*p*(MOD+1-a[j][i])%MOD; } if(!p) continue; for(auto &u:f[w^1]) add(f[w][u.x+s],1ll*u.y*p%MOD); } } for(auto &u:f[w]) add(ans,1ll*u.y*u.x.y[0]%MOD); printf("%d\n",ans); }