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种:

  1. 只被\(min\)覆盖;
  2. 只被\(mex\)覆盖;
  3. \(min\)\(mex\)覆盖;
  4. 没有被覆盖;

初始化\(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\)

image-20251128110546032

这时候\(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;
}
posted @ 2025-11-28 14:27  alij  阅读(0)  评论(0)    收藏  举报