1. 程式人生 > 其它 >[省選集訓2022] 模擬賽13

[省選集訓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);
}