loading

P12558 [UOI 2024] Heroes and Monsters

题意

给定长度为 \(n\) 的数组 \(a,b\),保证 \(\{a\},\{b\}\) 的元素构成 \(1\sim 2n\) 的排列。

现在你需要让 \(a\)\(b\) 每个元素之间匹配,设 \(a_i\)\(b_{p_i}\) 匹配,再设 \(S=\{x|a_x>b_{p_x}\}\)

\(q\) 次询问,每次询问查询 \(l\le |S|\le r\)\(S\) 个数。

\(n,q\le 5\times10^3\)

分析

如果我们指定某些数在 \(S\) 内,其他数不在,那么我们贪心的将在 \(S\) 内的数跟前面的数匹配,不在 \(S\) 内的数跟后面匹配,显然是正确的。据此我们可以写出一个 \(O(n^3)\) 的做法:外层枚举 \(|S|=k\),设 \(f_{i,j}\) 表示前 \(i\)\(a_i\),在 \(S\) 内的有 \(j\) 个,转移显然。

然而我们发现 \(i,j,k\) 没有一个是能省的,转移也不好优化。考虑发掘性质,假设我们知道了 \(k\),这个时候必然有:对于所有 \(a_i<b_k\) 的数,它们跟后面的数匹配不会产生任何限制。\(a_i>b_k\) 同理,我们只需要考虑 \(a_i<b_k\) 跟前面的匹配的限制和 \(a_i>b_k\) 跟后面的匹配的限制,单个限制是能很好的 \(O(n^2)\) 做的(此时 DP 不用记 \(k\)),最后外层枚举 \(k\),将前缀的 DP 值跟后面的 DP 值合并起来即可。复杂度 \(O(n^2)\)

int n,Q,a[maxn],b[maxn];
int f[maxn][maxn],g[maxn][maxn];
int ans[maxn];
inline void solve_the_problem(){
	n=rd();
	rep(i,1,n)a[i]=rd();
	rep(i,1,n)b[i]=rd();
	sort(a+1,a+n+1),sort(b+1,b+n+1);
	Q=rd();
	f[0][0]=1;
	rep(i,1,n)rep(j,0,n)if(f[i-1][j]){
		adder(f[i][j],f[i-1][j]);
		if(a[i]>b[j+1])adder(f[i][j+1],f[i-1][j]);
	}
	g[n+1][0]=1;
	per(i,n,1)rep(j,0,n)if(g[i+1][j]){
		adder(g[i][j],g[i+1][j]);
		if(a[i]<b[n-j])adder(g[i][j+1],g[i+1][j]);
	}
	rep(i,0,n){
		int p=lower_bound(a+1,a+n+1,b[i])-a;
		rep(j,0,i)adder(ans[i],1ll*f[p-1][j]*g[p][(n-p+1)-(i-j)]%mod);
	}
	while(Q--){
		int l=rd(),r=rd(),sum=0;
		rep(i,l,r)adder(sum,ans[i]);
		write(sum);
	}
}
posted @ 2025-06-02 12:40  dcytrl  阅读(49)  评论(3)    收藏  举报