Codeforces Round 1066 (Div. 1 + Div. 2)
Codeforces Round 1066 (Div. 1 + Div. 2)
C
构造题
给定\(q\)个操作\((c,l,r)\):
- 如果\(c=1\),那么\(\min_{i=l}^r{p_i}=k\);
- 如果\(c=2\),那么\(mex(l,r)=k\);
对于一个位置\(i\),可以分为下面4种:
- 只被\(min\)覆盖;
- 只被\(mex\)覆盖;
- 被\(min\)和\(mex\)覆盖;
- 没有被覆盖;
初始化\(ans[i]=-1\)。
对于4,我们显然是可以随便填的,初始化为\(1e9\);
对于3,只能填大于\(k\)的,所以我们填下\(1e9\)即可;
对于1,我们都填\(k\)就好;
对于2,我们还需要考虑由多个\(mex\)覆盖,这时候需要对\(mex\)的区间进行一个排序,贪心的使得一个区间使用最少的位置填完\(0,1,\dots,k-1\),对于剩下的位置,我们填\(k+1\)。
所以我们对于\(mex\)的区间,我们先用\(vis\)数组标记\([0,k-1]\)填了那些数,之后我们贪心的从前往后填\([0,k-1]\),我们填的是那些\(ans[i]=-1\or ans[i]=k+1\)的位置可以进行修改。
void solve() {
int n,k,q;
cin >> n >> k >> q;
vector<array<int,2>> a(n+1),b(n+1);
vector<int> f(n+1);
for(int i=1;i<=q;i++){
int c,l,r;
cin >> c >> l >> r;
if(c==1) a.push_back({l,r});
else b.push_back({l,r});
for(int j=l;j<=r;j++){
f[j]|=(1<<(c-1));
}
}
vector<int> ans(n+1,-1);
for(int i=1;i<=n;i++){
if(f[i]==0){
ans[i]=1e9;
}else if(f[i]==1){
ans[i]=k;
}else if(f[i]==3){
ans[i]=1e9;
}
}
sort(b.begin(),b.end(),[&](array<int,2> x,array<int,2> y){
if(x[0]!=y[0]) return x[0]<y[0];
else return x[1]<y[1];
});
//对于[l,r]我们需要填ans[i]==-1的位置
for(auto[l,r]:b){
vector<int> vis(k+1,0);
for(int i=l;i<=r;i++){
if(ans[i]>=0&&ans[i]<k){
vis[ans[i]]=1;
}
}
int j=0;
for(int i=l;i<=r;i++){
if(ans[i]==-1||ans[i]==k+1){
while(j<k&&vis[j]) j++;
if(j<k) ans[i]=j;
else ans[i]=k+1;
vis[j]=1;
}
}
}
for(int i=1;i<=n;i++){
cout << ans[i] << " \n"[i==n];
}
}
D
给定\(n\)个数\(a_i\),现在有一个数\(p(l\leq p \leq r)\),现在对于每一个\(a_i\),我们可以有三种操作:
- 跳过;
- 声称\(a_i\leq p\),如果是对的,那么赚取\(|p-a_i|\);否则损失\(|p-a_i|\);
- 声称\(a_i\geq p\),如果是对的,那么赚取\(|p-a_i|\);否则损失\(|p-a_i|\);
现在问可以赚的最小值,也就是能够保证的能够赚取的值,也就是\(ans=\min_{p=l}^{r}{|p-a_i|}\)。
\(sol\)
我们先来分析一下对于\(p\)可以任意取得情况下能够获得的最小值。
对于两个点\(a,b\),不妨令\(a\leq b\),那么\(min(|x-a|+|x-b|)\),容易得出\(a\leq x\leq b\)时,取得最小值\(|b-a|\);
对于三个点\(a,b,c\):
这时候\(x=b\)时是最优的,满足\(ab和bc\)同时最小。
对于四个点\(a,b,c,d\):\(b\leq x\leq c\)时可以取得最小值;
\(\dots\)
我们可以发现对于奇数个点,我们取到中间那个点时\(x=a_{(n+1)/2}\),取得最小值;
对于偶数个点,\(a_{n/2}\leq x \leq a_{n/2+1}\)时都可以取得最小值。
证明:
序列\({a_1,a_2,a_3,\dots,a_n}\),满足\(a_1\leq a_2\leq\dots\leq a_n\),\(f(x)=\sum_{i=1}^{n}{|x-a_i|}\),我们目标就是最小化\(f(x)\)。
令\(L(x)=\sum_{i=1}^{n}{[a_i< x]},R(x=\sum_{i=1}^n{[x<a_i]})\),也就是对于微小增量\(\delta\)的时候,即不会在增加后遇到点,那么这时的变化值为\(g(x)=(L(x)-R(x))\delta\)。
当\(x<a_1\)时,\(L(x)=-n,R(x)=0\),这时候\(g(x)<0\),所以\(f(x)\)在\((-\inf,a_1]\)单减;
手玩一下可以容易发现\(g(x)\)是先单减后单增,所以我们取到\(g(x)=0\)处的\(x\)位置,即\(L(x)=R(x)\),当\(n\)为奇数时,\(x=a_{\frac{n+1}{2}}\);当\(n\)为偶数时,\(x\in[a_{\frac{n}{2}},a_{\frac{n}{2}+1}]\)。
所以这道题可以求出中位数区间\([left,right]\),然后讨论\([l,r]\)的关系即可。
void solve() {
int n,l,r;
cin >> n >> l >> r;
vector<int> a(n+1);
for(int i=1;i<=n;i++){
cin >> a[i];
}
sort(a.begin()+1,a.end());
int left=a[(n+1)/2],right=a[n/2+1];
int x=0;
if(l>=left&&l<=right){
x=l;
}else if(left<=r&&right>=r){
x=r;
}else if(l<=left&&r>=right){
x=left;
}else if(l>right){
x=l;
}else if(r<left){
x=r;
}
int ans=0;
for(int i=1;i<=n;i++){
ans+=abs(x-a[i]);
}
cout << ans << endl;
}

浙公网安备 33010602011771号