qoj9698. Twenty-two solution

考虑全局先对 \(c_1\)\(\min\),中间执行了一些操作 \(1\),再对 \(c_2\) 全局取 \(\min\),如果 \(c_1\ge c_2\) 那么 \(c_1\) 就是无效操作。那么操作 \(2\) 中可以选出必须包含全局 \(\min\) 的一些 \(c\),记为集合 \(S\),按照递增顺序排列并将操作序列分段。注意到 \(\min(\max(a,b),c)=\max(\min(a,c),\min(b,c))\),先将所有 \(a_i\) 对操作 \(2\) 最小的 \(c\) (称为 \(mc\))取 \(\min\),再执行操作 \(1\)\(\min(mc,c)\)\(\max\) 的操作。问题转化为:每个操作 \(1\) 可以选取某个操作 \(2\)\(c'\),将它自身的 \(c\) 变成 \(\min(c,c')\),随后只执行操作 \(1\) 问可能的序列种类数。

\(\max\) 可以看成在从大到小扫描线值域中进行的覆盖操作。假设当前决策值 \(=x\) 的操作,则关心的是一个 \(01\) 串以及这个 \(01\) 串在 \(>x\) 位置对应的方案数。然后你选取一段将这一段覆盖为 \(1\),此时这一段中原先为 \(1\) 的极长段就是 \(\ge x+1\) 且全为 \(1\) 的子问题。不难想到状态是:仅考虑被 \([l,r]\) 包含的操作 \(1\),所有数 \(\ge x\) 的方案数 \(dp_{l,r,x}\)

转移的流程是:对于 \(=x\) 操作后新的极长 \(1\)\([l,r]\),钦定一个点集 \(S\) 表示这一次被染成 \(x\),若 \(S\) 合法则将这个 \(S\) 的贡献(相邻的每一段的 \(dp_{x,l',r'}\) 乘积)加入 \(dp_{l,r,x}\)。而 \(S\) 合法当且仅当,其是所有可以被修改为 \(x\) 的操作 \(1\) 线段 \([l',r']\) 的并集 \(U\) 的子集。注意要求 \([l',r']\subseteq [l,r]\),固定 \(l\) 扫描一下 \(r\),对集合 \(S\) 的贡献进行 dp,对于一个 \(x\) 的复杂度是 \(n^4\)。原有 \(a_i\) 的限制相当于要求某些元素必须不在 \(S\) 中,或者提供一些元素可以在 \(S\) 中。最终答案就是 \(dp_{1,n,1}\),由于需要对所有 \(x\) 都做一遍,时间复杂度 \(\mathcal O(n^5)\)。卡常后可以过。

优化:对于 \(l,r\) 单独跑一次 dp 的时间太慢了,考虑直接固定 \(l\)\(r\) 求 dp 值,每次允许再来 \(\mathcal O(n)\) 的枚举。每次 \(r\to r+1\) 相当于把一些原先不允许加入 \(S\) 的改成可以加入 \(S\),而每个位置只会被修改一次,再做一些处理可以把复杂度变成 \(\mathcal O(n^4)\)

#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define uint unsigned
#define pb push_back
#define mkp make_pair
#define fi first
#define se second
#define inf 1000000000
#define infll 1000000000000000000ll
#define pii pair<int,int>
#define rep(i,a,b,c) for(int i=(a);i<=(b);i+=(c))
#define per(i,a,b,c) for(int i=(a);i>=(b);i-=(c))
#define F(i,a,b) for(int i=a,i##end=b;i<=i##end;i++)
#define dF(i,a,b) for(int i=a,i##end=b;i>=i##end;i--)
#define lowbit(x) ((x)&(-(x)))
#define HH printf("\n")
#define eb emplace_back
#define SZ(x) ((int)x.size())
#define all(x) x.begin(),x.end()
using namespace std;
bool ST;
template<typename T>inline void chkmax(T &x,const T &y){ x=std::max(x,y); }
template<typename T>inline void chkmin(T &x,const T &y){ x=std::min(x,y); }
const int mod=998244353,maxn=155;
inline int qpow(int x,ll y){ int res=1; for(;y;y>>=1,x=1ll*x*x%mod)if(y&1)res=1ll*res*x%mod; return res; }
inline void inc(int &x,const int y){ x=(x+y>=mod)?(x+y-mod):(x+y); }
inline void dec(int &x,const int y){ x=(x>=y)?(x-y):(x+mod-y); }
inline int add(const int x,const int y){ return (x+y>=mod)?(x+y-mod):(x+y); }
inline int sub(const int x,const int y){ return (x>=y)?(x-y):(x+mod-y); }
int n,m1,m2,a[maxn];
vector<int>vec;
bool fl[maxn][maxn][maxn];
int dp[maxn][maxn],f[maxn][maxn],tr[maxn];
bool vis[maxn];
vector<int>qu[maxn];
void solve(){
	cin>>n>>m1>>m2;
	F(i,1,n)cin>>a[i];
	F(_,1,m1){ int x;cin>>x,vec.push_back(x); }
	sort(all(vec)),vec.erase(unique(all(vec)),vec.end());
	const int mc=vec[0];
	F(i,1,n)chkmin(a[i],mc);
	F(_,1,m2){
		int l,r,x;cin>>l>>r>>x,fl[x][l][r]=1;
		for(int i:vec)if(i<x)fl[i][l][r]=1;
		F(i,l,r)chkmax(a[i],min(x,mc));
	}
	F(i,0,n)dp[i+1][i]=1;
	dF(x,n,1){
		F(i,0,n+1)F(j,0,n+1)f[i][j]=dp[i][j];
		F(i,1,n)qu[i].clear();
		F(r,1,n)F(l,1,r)if(fl[x][l][r])qu[r].push_back(l);
		F(l,1,n){
			F(i,0,n)vis[i]=0;
			vis[l-1]=1;
			F(r,l,n){
				if(a[r]==x)vis[r]=1;
				for(int i:qu[r])if(l<=i){
					F(j,i,r)vis[j]=1;
					break;
				}
				F(i,l-1,r)tr[i]=(i==l-1);
				F(i,l,r)if(a[i]>x)vis[i]=0;
				F(i,l,r)if(vis[i]){
					__int128 sum=0;
					#pragma GCC unroll(16)
					F(j,l-1,i-1)if(vis[j])sum+=1ll*tr[j]*f[j+1][i-1];
					if(sum)tr[i]=sum%mod,inc(dp[l][r],1ll*tr[i]*f[i+1][r]%mod);
				}
			}
		}
	}
	cout<<dp[1][n]<<'\n';
}
bool ED;
signed main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int zsy=1;
	F(____,1,zsy)solve();
	cerr<<"time used: "<<(double)clock()/CLOCKS_PER_SEC<<endl;
	cerr<<"memory used: "<<abs(&ST-&ED)/1024.0/1024.0<<" MB"<<endl;
}
// g++ qoj9698.cpp -o a -std=c++14 -O2

posted on 2025-08-28 14:30  nullptr_qwq  阅读(24)  评论(0)    收藏  举报