在空无一物的时光深处

题目链接

模拟赛遇见的题,记录一下犯了一整场的唐。赛后 5min 52pts😭。

染色必定是一个区间。

这种题目套路的,正难则反,现在相当于每个位置只会被第一次染色影响,不存在覆盖情况。

首先不难发现,对于第 \(i\) 种颜色想要更新画板,必定为第 \(i\) 次涂完色更新了染色区间。

于是不难想到 \(f_{l,r,k,0/1}\) 表示当前染色区间为 \([l,r]\),笔刷位置在 \(k\),其中本次染色是否更新了染色区间。

但是发现会算重。如果当前从之前某次操作的 \([l,r,k'',1]\)\([l,r,k',1]\) 一路直接或间接转移到了 \([l,r,k]\) 这样虽然到 \([l,r,k]\) 时画板状态一致却被计算了多次。

为了不让此类不更新画板的操作计错。直接考虑枚举上次的有效操作(更新了画板状态的操作)。

于是现在只有更新了画板的操作对应的状态有用,考虑设计 \(f_{i,l,r,0/1}\) 表示第 \(i\) 次操作后,向左 / 向右拓展了染色区间,则当前笔刷在 \(l/r\)

假设当前第 \(i\) 次操作前笔刷在位置 \(x\),考虑能继承哪些状态 \([j,l,r,0/1]\) 的方案数。假设 \([j,l,r,0/1]\) 对应笔刷位置在 \(y\),则就是要满足能通过 \((i,j)\) 区间内的操作在不超出 \([l,r]\) 的条件下从 \(y\) 走到 \(x\)(从 \(x\)\(y\) 等价)。

考虑暴力状态 \(w_{i,j,l,r,x,y}\) 表示 \((i,j)\) 的操作不超出 \([l,r]\)\(x\)\(y\) 的可行性。这样状态数就是 \(O(n^6)\),考虑把 \(r\) 这一维用状态值表示。

即设计状态 \(w_{i,j,l,x,y}\) 表示 \((i,j)\) 的操作不到 \(l\) 的左边,从 \(x\)\(y\) 的过程中经过最大位置的最小可能,判断是否满足 \(w_{i,j,l,x,y}\le r\) 即可。

在第 \(i\) 次转移前 \(O(m)\) 枚举 \(l\)\(O(n^2m)\) 预处理所有 \(w_{i,j,l,x,y}\),所以不用 \(i\) 这维,这里的复杂度是 \(O(n^3m^2)\)。之后转移 \(f\) 是简单的,需要注意的就是从 \([n,l,r]\) 转移过来时无论是笔刷是在 \(l\) 还是 \(r\) 画板状态都一样,需要去除贡献。

code
#include<bits/stdc++.h>
#define fin(x) freopen(#x".in","r",stdin)
#define fout(x) freopen(#x".out","w",stdout)
#define fr(x) fin(x),fout(x);
#define Fr(x,y) fin(x),fout(y)
#define INPUT(_1,_2,FILE,...) FILE
#define IO(...) INPUT(__VA_ARGS__,Fr,fr)(__VA_ARGS__)
using namespace std;
#define mp make_pair
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define cfast ios::sync_with_stdio(false);cin.tie(0),cout.tie(0)
#define ll long long
#define ull unsigned long long
#define intz(x,y) memset((x),(y),sizeof((x)))
char *p1,*p2,buf[100000];
#define nc() (p1==p2 && (p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
#define tup(x) array<int,(x)>
inline ll read(){
    ll x=0,f=1;char ch=nc();
    while(ch<48||ch>57){if(ch=='-')f=-1;ch=nc();}
    while(ch>=48&&ch<=57)x=x*10+ch-48,ch=nc();
   	return x*f;
}
const int N=65,M=155,mod=998244353;
#define int ll
int f[M][N][N][2],c[M],w[M][N][N][N],n,m;
void work(int id,int l){//钦定 l,预处理 w
	for(int i=id;i<=n;i++)
		for(int x=1;x<=m;x++)
			for(int y=1;y<=m;y++)
				w[i][l][x][y]=1e9;
	for(int i=l;i<=m;i++)w[id+1][l][i][i]=i;
	for(int i=id+2;i<=n;i++)
		for(int st=l;st<=m;st++)//st -> x
			for(int j=l;j<=m;j++){//j -> y
				int L=j-c[i-1]+1,R=j+c[i-1]-1;
				if(L>=l)w[i][l][st][L]=min(w[i][l][st][L],max(w[i-1][l][st][j],L));			
				if(R<=m)w[i][l][st][R]=min(w[i][l][st][R],max(w[i-1][l][st][j],R));
			}
}
inline void UesugiErii(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>c[i];
	for(int i=1;i+c[n]-1<=m;i++){
		int l=i,r=i+c[n]-1;
		f[n][l][r][0]=f[n][l][r][1]=1;
	}
	for(int i=n-1;i;i--){
		for(int l=1;l<=m;l++)work(i,l);
		for(int j=i+1;j<=n;j++)
			for(int l=1;l<=m;l++)
				for(int r=l;r<=m;r++)
					for(int k=l;k<=r;k++){
						int L=k-c[i]+1,R=k+c[i]-1;
						int s=(f[j][l][r][0]*(w[j][l][k][l]<=r)+f[j][l][r][1]*(w[j][l][k][r]<=r))%mod;
						if(L>=1&&L<l)(f[i][L][r][0]+=s)%=mod;
						if(R<=m&&R>r)(f[i][l][R][1]+=s)%=mod;
						if(j==n&&(w[j][l][k][l]<=r)&&(w[j][l][k][r]<=r)){//去除 [n,l,r] 的多余贡献
							if(L>=1&&L<l)(f[i][L][r][0]+=mod-f[j][l][r][1])%=mod;
							if(R<=m&&R>r)(f[i][l][R][1]+=mod-f[j][l][r][0])%=mod;
						}
					}
	}
	int ans=0;
	for(int l=1;l<=m;l++)work(0,l);
	for(int j=1;j<=n;j++)
		for(int l=1;l<=m;l++)
			for(int r=l;r<=m;r++){
				bool fl=0,fr=0;
				for(int k=l;k<=r;k++)
					fl|=(w[j][l][k][l]<=r),fr|=(w[j][l][k][r]<=r);
				if(fl)ans=(ans+f[j][l][r][0])%mod;
				if(fr&&j!=n)ans=(ans+f[j][l][r][1])%mod;		
			}
	cout<<ans;
}
signed main(){
	IO(stare);
	cfast;
	int _=1;//cin>>_;
	for(;_;_--)UesugiErii();
	return 0;
}

然后发现这个 \(w\) 的预处理不弱于 DAG 每次询问只能经过编号在 \([l,r]\) 内的点时的可达性。然后想了一场不会优化。

实际上这些状态只跟染色区间的长度有关,因为区间位置不会影响其转移。最后统计答案时乘上 \(m-len+1\) 表示区间可能的位置即可。

所以直接更改状态 \(f_{i,j,0/1}\) 表示第 \(i\) 次操作后染色区间长度为 \(j\),拓展了左端点 / 右端点。钦定当前长为 \(l\) 的染色区间为 \([1,l]\)\(w_{i,j,x,y}\) 表示 \((i,j)\) 间操作不到 \(1\) 左边从 \(x\)\(y\) 的过程中经过的最大位置的最小可能。判断是否满足 \(w_{i,j,x,y}\le l\) 即可。

同理预处理 \(w\),但是不需要枚举钦定 \(l\) 所有少了 \(O(m)\)。转移 \(f\) 时只用枚举区间长度而非区间 \([l,r]\) 同样少了 \(O(m)\)

故总复杂度 \(O(n^2m^2)\)

#include<bits/stdc++.h>
#define fin(x) freopen(#x".in","r",stdin)
#define fout(x) freopen(#x".out","w",stdout)
#define fr(x) fin(x),fout(x);
#define Fr(x,y) fin(x),fout(y)
#define INPUT(_1,_2,FILE,...) FILE
#define IO(...) INPUT(__VA_ARGS__,Fr,fr)(__VA_ARGS__)
using namespace std;
#define mp make_pair
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define cfast ios::sync_with_stdio(false);cin.tie(0),cout.tie(0)
#define ll long long
#define ull unsigned long long
#define intz(x,y) memset((x),(y),sizeof((x)))
char *p1,*p2,buf[100000];
#define nc() (p1==p2 && (p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
#define tup(x) array<int,(x)>
inline ll read(){
    ll x=0,f=1;char ch=nc();
    while(ch<48||ch>57){if(ch=='-')f=-1;ch=nc();}
    while(ch>=48&&ch<=57)x=x*10+ch-48,ch=nc();
   	return x*f;
}
const int N=155,mod=998244353;
#define int ll
int f[N][N][2],c[N],w[N][N][N],n,m;
void work(int id){
	for(int i=id;i<=n;i++)
		for(int x=1;x<=m;x++)
			for(int y=1;y<=m;y++)
				w[i][x][y]=1e9;
	for(int i=1;i<=m;i++)w[id+1][i][i]=i;
	for(int i=id+2;i<=n;i++)
		for(int st=1;st<=m;st++)
			for(int j=1;j<=m;j++){
				int L=j-c[i-1]+1,R=j+c[i-1]-1;
				if(L>=1)w[i][st][L]=min(w[i][st][L],max(w[i-1][st][j],L));			
				if(R<=m)w[i][st][R]=min(w[i][st][R],max(w[i-1][st][j],R));
			}
}
inline void UesugiErii(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>c[i];
	f[n][c[n]][0]=f[n][c[n]][1]=1;
	for(int i=n-1;i;i--){
		work(i);
		for(int j=i+1;j<=n;j++)
			for(int l=1;l<=m;l++)
				for(int k=1;k<=l;k++){
					int L=k-c[i]+1,R=k+c[i]-1;
					int s=(f[j][l][0]*(w[j][k][1]<=l)+f[j][l][1]*(w[j][k][l]<=l))%mod;
					if(L<=0)(f[i][l-L+1][0]+=s)%=mod;
					if(R>l)(f[i][R][1]+=s)%=mod;
					if(j==n&&(w[j][k][1]<=l)&&(w[j][k][l]<=l)){
						if(L<=0)(f[i][l-L+1][0]+=mod-f[j][l][1])%=mod;
						if(R>l)(f[i][R][1]+=mod-f[j][l][0])%=mod;
					}
				}
	}
	int ans=0;
	for(int l=1;l<=m;l++)work(0);
	for(int j=1;j<=n;j++)
		for(int l=1;l<=m;l++){
			bool fl=0,fr=0;
			for(int k=1;k<=l;k++)
				fl|=(w[j][k][1]<=l),fr|=(w[j][k][l]<=l);
			if(fl)ans=(ans+f[j][l][0]*(m-l+1)%mod)%mod;
			if(fr&&j!=n)ans=(ans+f[j][l][1]*(m-l+1)%mod)%mod;		
		}
	cout<<ans;
}
signed main(){
	IO(stare);
	cfast;
	int _=1;//cin>>_;
	for(;_;_--)UesugiErii();
	return 0;
}

这也太蠢了。

posted @ 2025-11-19 18:31  Uesugi1  阅读(15)  评论(0)    收藏  举报