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);
}
}

浙公网安备 33010602011771号