在空无一物的时光深处
题目链接。
模拟赛遇见的题,记录一下犯了一整场的唐。赛后 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;
}
这也太蠢了。

浙公网安备 33010602011771号