Educational Codeforces Round 76 部分题解

A. Two Rival Students

题意:

一排有 \(n\) 个学生,两个竞争对手分别在位置 \(a\)\(b\)。每次操作可以交换相邻两个学生,最多进行 \(x\) 次操作。求操作后两个竞争对手之间可能的最大距离(即 \(|p-s|\))。

思路:

初始距离为 \(d = |a-b|\)。每次交换可以让一个学生向远离对方的方向移动一步,从而增加 \(1\) 距离。最多可以增加 \(x\) 距离,但最大距离不超过 \(n-1\)(即一个在左端,一个在右端)。因此答案为 \(\min(d + x, n-1)\)

代码

点击查看代码
void solve() {
	int n, x, a, b;
	cin >> n >> x >> a >> b;
	int d = abs(a - b);
	int ans = min(d + x, n - 1);
	cout << ans << endl;
}

B. Magic Stick

题意:

给定正整数 \(x\)\(y\),可以对当前数 \(a\) 进行两种操作:

1. 若 \(a\) 为偶数,可变为 \(\frac{3a}{2}\)

2. 若 \(a > 1\),可变为 \(a-1\)

问能否通过任意次操作(包括零次)从 \(x\) 得到 \(y\)

思路:

  • \(x \ge y\),则总可通过多次操作2(减1)得到 \(y\),因为减的过程中每个数均大于1。
  • \(x < y\),则需分类讨论:
    • \(x = 1\):只能得到 \(1\),故不可达。
    • \(x = 2\):仅当 \(y = 3\) 时可达(\(2 \rightarrow 3\)),其余 \(y > 3\) 不可达。
    • \(x = 3\):最大只能得到 \(3\),故 \(y > 3\) 不可达。
    • \(x \ge 4\):总可通过操作1和操作2增长到任意大的数,再通过操作2减到 \(y\),故可达。

代码

点击查看代码

void solve() {
    int x, y;
    cin >> x >> y;
    if (x >= y) {
        cout << "YES" << endl;
        return;
    }
    if (x == 1) {
        cout << "NO" << endl;
        return;
    }
    if (x == 2 && y == 3) {
        cout << "YES" << endl;
        return;
    }
    if (x >= 4) {
        cout << "YES" << endl;
        return;
    }
    cout << "NO" << endl;
}

C. Dominated Subarray

题意:

给定一个长度为 \(n\) 的数组 \(a\),求最短的连续子数组,使得子数组中存在某个元素出现的次数严格大于其他任意元素出现的次数。如果不存在这样的子数组,输出 \(-1\)

思路:

不难发现,合法区间的左右端点的值必定相同。
有结论:枚举相同值作为端点,直接记录最小区间长度即为所求答案,保证最后的答案一定合法。
如果我们记录的当前区间非法,则区间内(不包括两端点)必定存在出现次数大于1的数字,则该区间肯定会被更小的区间所更新。
结论得证。

代码

点击查看代码
int a[N];
vector<int>g[N];
void solve() {
	int n,x,y;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		g[a[i]].push_back(i);
	}
	int mx=n+1;
	
	for(int i=1;i<=n;i++){
		for(int j=1;j<g[i].size();j++){
			mx=min(mx,g[i][j]-g[i][j-1]+1);
		}
		
		g[i].clear();
	}
	if(mx<n+1)cout<<mx<<endl;
	else cout<<-1<<endl;
}

D. Yet Another Monster Killing Problem

题意:

\(n\) 个怪物排成一队,第 \(i\) 个怪物的战斗力为 \(a_i\)。你有 \(m\) 个英雄,每个英雄有两个属性:战斗力 \(p_j\) 和耐力 \(s_j\)(每天最多击败 \(s_j\) 个怪物)。每天可以选择一个英雄,他从第一个未被打败的怪物开始连续战斗,只要当前怪物的战斗力不超过英雄的战斗力,就可以击败它并继续下一个,直到达到英雄的耐力限制或所有怪物都被击败。如果遇到战斗力超过英雄战斗力的怪物,则英雄撤退,当天结束。同一个英雄可以重复使用。问最少需要多少天才能打败所有怪物,如果无法完成输出 \(-1\)

思路:

对于每个耐力值 \(s\),我们只关心所有耐力至少为 \(s\) 的英雄中的最大战斗力,记为 \(mx[s]\)
预处理 \(mx\) 数组:读入英雄时更新 \(mx[s] = \max(mx[s], p)\),然后从后往前递推,使 \(mx[s] = \max(mx[s], mx[s+1])\)
检查是否可能:如果存在某个 \(a_i > mx[1]\),说明没有一个英雄能单独打败这个怪物,直接输出 \(-1\)
用ST表预处理区间最大值。
从第一个怪物开始,每次二分找出当前能连续打败的最长段长度 \(L\),满足该段内怪物的最大战斗力 \(\le mx[L]\)。然后天数加一,跳过这段,继续直到覆盖所有怪物。

代码

点击查看代码
int Logn[N];
int f[22][N];

int a[N], b[N];
int p[N], s[N];
int qry(int l, int r) {
	int k = Logn[r - l + 1];
	return max(f[k][l], f[k][r - (1 << k) + 1]);
}
void solve() {
	int n, m;
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}
	cin >> m;
	for (int i = 1; i <= n + 1; i++) b[i] = 0;
	for (int i = 1; i <= m; i++) {
		int p, s;
		cin >> p >> s;
		if (p > b[s]) b[s] = p;
	}
	for (int i = n; i >= 1; i--) {
		b[i] = max(b[i], b[i + 1]);
	}
	for (int i = 1; i <= n; i++) {
		if (a[i] > b[1]) {
			cout << -1 << endl;
			return;
		}
	}
	Logn[0] = -1;
	for (int i = 1; i <= n; i++) Logn[i] = Logn[i >> 1] + 1;
	for (int i = 1; i <= n; i++) f[0][i] = a[i];
	for (int j = 1; j <= Logn[n]; j++) {
		for (int i = 1; i + (1 << j) - 1 <= n; i++) {
			f[j][i] = max(f[j - 1][i], f[j - 1][i + (1 << (j - 1))]);
		}
	}
	int ans=0,p=1;
	while(p<=n){
		int l=1,r=n-p+1,res=0;
		while(l<=r){
			int mid=(l+r)>>1;
			int cur=qry(p,p+mid-1);
			if(cur<=b[mid]){
				res=mid;
				l=mid+1;
			}else{
				r=mid-1;
			}
		}
		ans++;
		p+=res;
	}
	cout<<ans<<endl;

}

E. The Contest

题意:

给定三个集合,包含 \(1\)\(n\) 的所有整数。每次操作可以将一个数从一个集合移到另一个集合。求最小操作次数,使得第一个集合是 \(\{1,2,\dots,x\}\)(前缀),第三个集合是 \(\{y,y+1,\dots,n\}\)(后缀),第二个集合是剩下的数(中间部分)。允许某些集合为空。

思路:

将每个数 \(i\) 标记它当前所属的集合(\(1,2,3\)),得到一个长度为 \(n\) 的序列。目标状态等价于将序列变为非递减的(即先全 \(1\),再全 \(2\),最后全 \(3\))。问题转化为:最少改变多少个位置的值,使得序列非递减。

\(pref[i][c]\) 表示前 \(i\) 个位置中值为 \(c\) 的个数。假设最终前缀长度为 \(x\),中间长度为 \(y-x\),后缀长度为 \(n-y\),则正确位置的数量为:

\[pref[x][1] + (pref[y][2] - pref[x][2]) + (pref[n][3] - pref[y][3]) \]

\(f(x,y) = pref[x][1] - pref[x][2] + pref[y][2] - pref[y][3] + pref[n][3]\)

对于每个 \(x\),只需最大化 \(pref[y][2] - pref[y][3] \ (y \ge x)\),可以用后缀最大值优化。答案即为 \(n\) 减去最大正确位置数。

代码

点击查看代码
int a[N], p1[N],p2[N],p3[N];
int b[N], s[N];

void solve() {
	int k1,k2,k3;
	cin>>k1>>k2>>k3;
	int n=k1+k2+k3;
	for(int i=0;i<k1;i++){
		int x;
		cin>>x;
		a[x]=1;
	}
	for(int i=0;i<k2;i++){
		int x;
		cin>>x;
		a[x]=2;
	}
	for(int i=0;i<k3;i++){
		int x;
		cin>>x;
		a[x]=3;
	}
	for(int i=1;i<=n;i++){
		p1[i]=p1[i-1]+(a[i]==1);
		p2[i]=p2[i-1]+(a[i]==2);
		p3[i]=p3[i-1]+(a[i]==3);
	}
	for(int i=0;i<=n;i++) b[i]=p2[i]-p3[i];
	s[n]=b[n];
	for(int i=n-1;i>=0;i--) s[i]=max(b[i],s[i+1]);
	int mx=0;
	for(int x=0;x<=n;x++){
		int val=p1[x]-p2[x]+s[x]+p3[n];
		mx=max(mx,val);
	}
	cout<<n-mx<<endl;
}
posted @ 2026-02-05 11:59  ptlks  阅读(0)  评论(0)    收藏  举报