CF Round 1015 题解合集
好像不难,问题是时间不够。
D
来搞笑的不是。
考虑二分答案,相当于是 \(\le mid\) 的数都要被删除,所以我们把这些位置标为 \(1\),相当于是要用至多 \(m\) 条长度为 \(k\) 的线段覆盖所有为 \(1\) 的位置,从左往右贪即可。
复杂度 \(O(n \log n)\)。
E
首先考虑一个 \(O(n^3)\) 做法。
因为 \(\text{mex}(S)\) 可以被转化为,满足 \([0,k]\) 都在 \(S\) 中出现的 \(k\) 的个数,我们就用这个含义做。
于是考虑枚举区间 \([l,r]\),枚举 \(k\),我们希望求出有多少填数的方案,使得 \([0,k]\) 都在区间 \([l,r]\) 中出现过。
设总共能够填数的位置有 \(t\) 个,区间 \([l,r]\) 中,能够填数的位置有 \(a\) 个,未在 \([0,k]\) 中出现过的数有 \(b\) 个,那么合法填数方案个数为:
现在我们希望优化到 \(O(n^2)\)。
有一个想法是,我们枚举 \([l,r]\) 太憨了,不如去枚举 \(a\) 和 \(b\),然后计算有多少个区间。
但是因为 \(b\) 和 \(k\) 有关,可能不好做,所以我们进行一点转化:设 \(c\) 表示任意的 \(k\),满足 \([0,k-1]\) 都在区间 \([l,r]\) 中出现过,我们计算 \(a,c\) 相同的区间个数,记为 \(f_{a,c}\),那么最终答案为:
最后一项只和 \(a\) 有关,所以可以提前预处理:
现在考虑 \(f_{a,b}\) 的计算。
还是枚举区间 \([l,r]\),然后记录当前区间的 \(a\) 和最大的 \(c\),每次相当于对 \(f_a\) 做一个 \([0,c]\) 的前缀加,差分即可。
复杂度 \(O(n^2)\)。
F
感觉是简单题,但是好像并不简单。
首先考虑什么样的 \(b\) 是合法的,设每个数在 \(a\) 中的出现位置为 \(posa\),在 \(b\) 中的出现位置为 \(posb\),于是 \(b\) 合法,当且仅当对于任意二元组 \((c,d)\),满足 \(c<d,posa_c<posa_d\),都有 \(posb_c<posb_d\)。
很好感性理解,因为如果小的数在前面,大的数在后面,那么无论如何它们两个的相对位置不会改变。
于是考虑,对于每一个数,它能够填的位置,一定构成一段区间,所以我们尝试去求出这个区间。做两个二维偏序,用线段树维护,就能求出每个数的合法区间 \(l,r\)。
现在的问题转化为,对于每一个数 \(x\),都有一个能够填的区间 \([l_x,r_x]\),询问一个合法填数方案。
我们将区间按 \(l_x\) 升序,\(r_x\) 降序排序,依次填数即可,如果不能满足全部限制即无解。
注意已经填数的位置,我们需要判断它是否在它的合法区间 \([l_x,r_x]\) 里,如果不在即无解。
复杂度 \(O(n \log n)\)。
G1
Alice and Bob 能不能消停会。
经过手玩样例,我们得到结论:
不会证明,但是根据博弈的过程感觉这就很对。
然后我们把 \(p\) 按下标奇偶分成两个集合,那么集合内部的数是两两取 max,集合外部的数是两两取 min。现在需要填数,满足两个集合的大小分别是 \(cnt_1\) 和 \(cnt_2\),最大化这个值。
于是考虑一个 DP,设 \(f_{i,j}\) 表示填了 \([1,i]\),第一个集合有 \(j\) 个数的最大价值,\(cnt_1,cnt_2\) 分别表示两个集合的大小,转移枚举当前这个数是一集合还是二集合:
注意已经填数的不需要再枚举,直接从对应集合转移就行。
复杂度 \(O(n^2)\)。
然后这个做法没有任何优化前途,根本做不了 G2,遗憾离场。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=1e18;
int t,n,c[5005],pos[5005],cnt1,cnt2,f[5005][5005];
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--){
cin>>n;
for(int i=1;i<=n;i++){
cin>>c[i];
pos[c[i]]=i;
}
cnt1=n/2;
cnt2=n-cnt1;
for(int i=0;i<=n;i++){
for(int j=0;j<=cnt1;j++){
f[i][j]=-inf;
}
}
f[0][0]=0;
for(int i=1;i<=n;i++){
for(int j=0;j<=i && j<=cnt1;j++){
if(pos[i]){
if(pos[i]&1) f[i][j]=f[i-1][j]+(cnt1-j)*i+(i-j)*i;
else if(j) f[i][j]=f[i-1][j-1]+(cnt2-(i-j))*i+j*i;
}else{
if(j) f[i][j]=max(f[i][j],f[i-1][j-1]+(cnt2-(i-j))*i+j*i);
f[i][j]=max(f[i][j],f[i-1][j]+(cnt1-j)*i+(i-j)*i);
}
}
}
cout<<f[n][cnt1]<<'\n';
for(int i=1;i<=n;i++){
pos[i]=0;
}
}
return 0;
}