Educational Codeforces Round 74 部分题解

A. Prime Subtraction

题意:

给定两个整数 x 和 y(保证 x > y)。你可以选择任意一个质数 p,并从 x 中减去任意次 p。问是否能通过这样的操作使 x 等于 y。

思路:

每次操作相当于选择一个质数 p,然后减去它的倍数。所以问题等价于:是否存在一个质数 p,使得 x-y 是 p 的倍数。由于任意大于 1 的整数都有质因子,因此只要 x-y > 1,就一定能找到这样的 p。而如果 x-y = 1,因为 1 没有质因子,所以不可能。因此只需判断 x-y 是否大于 1 即可。

代码

点击查看代码
void solve() {
	int x,y;
	cin>>x>>y;
	if(x-y==1){
		cout<<"NO"<<endl;
	}else{
		cout<<"YES"<<endl;
	}
}

B. Kill 'Em All

题意:

有一条无限长的数轴,原点 \(x=0\) 处有陷阱,任何怪物坐标 \(\le 0\) 时会被陷阱杀死。初始有 \(n\) 个怪物在正半轴,位置为 \(x_i\)。每次操作可以选择一个整数点 \(c\) 发射导弹:位于 \(c\) 的怪物直接死亡;坐标小于 \(c\) 的怪物左移 \(r\) 单位;坐标大于 \(c\) 的怪物右移 \(r\) 单位。求杀死所有怪物的最少操作次数。

思路:

将怪物坐标去重排序,从大到小贪心。设已经进行了 \(cnt\) 次操作,每次操作都会使所有小于攻击点的怪物左移 \(r\)。对于当前最大的坐标 \(x\),如果 \(x - cnt \cdot r > 0\),说明它还未被之前的操作波及致死,需要额外一次操作攻击它,此时 \(cnt\) 加一。最终 \(cnt\) 即为答案。

代码

点击查看代码
void solve() {
	int n,r;
	cin>>n>>r;
	a.clear();
	a.push_back(0);
	for(int i=1;i<=n;i++){
		int x;
		cin>>x;
		a.push_back(x);
	}
	sort(a.begin(),a.end());
	a.erase(unique(a.begin(),a.end()),a.end());
	n=a.size()-1;
	int L=1,R=n;
	int ans=0;
	while(L<=R){
		int mid=L+R>>1;
		if(a[n-mid]-mid*r<=0){
			ans=mid;
			R=mid-1;
		}else{
			L=mid+1;
		}
	}
	cout<<ans<<endl;
	
}

C. Standard Free2play

题意:

悬崖高度为 \(h\),从 \(1\)\(h\) 每个整数高度都有一个平台。平台有两种状态:隐藏或移出。初始时,有 \(n\) 个移出的平台,高度分别为 \(p_1 > p_2 > \cdots > p_n\)(其中 \(p_1 = h\)),角色站在高度 \(h\) 上。

角色每次可以拉动杠杆,使当前平台隐藏,同时切换正下方平台的状态(隐藏变移出,移出变隐藏)。若切换后下方平台为移出,则角色落到该平台;否则角色会下落,但最多只能安全下落 \(2\) 单位高度(即从 \(x\) 落到 \(x-2\) 安全,落到 \(x-3\) 或更低则死亡)。

此外,可以使用魔法水晶改变任意一个平台(除高度 \(h\) 外)的状态。求安全到达地面(高度 \(0\))所需的最少水晶数量。

思路:

直接模拟下落过程。设当前位置为 \(cur\)(初始为 \(h\)),每次判断:

  • \(cur-1\) 是移出平台:拉动杠杆后,\(cur\)\(cur-1\) 均变为隐藏。之后角色会落到 \(cur-2\)
    • \(cur-2\) 是移出平台,则直接落到该平台。
    • 否则需要使用一个水晶将 \(cur-2\) 变为移出,再落到该平台。
  • \(cur-1\) 不是移出平台:拉动杠杆后,\(cur\) 变为隐藏,\(cur-1\) 变为移出,角色落到 \(cur-1\),该操作可加速,直接落到(下一个移出平台+1)的位置。

模拟直到 \(cur =0\) 即可。时间复杂度为 \(O(n)\)

代码

点击查看代码
vector<int>a;
void solve() {
	int n,h;
	cin>>h>>n;
	a.clear();
	a.push_back(h);
	for(int i=1;i<=n;i++){
		int x;
		cin>>x;
		a.push_back(x);
	}
	a.push_back(0);
	int ans=0;
	for(int i=1;i<=n;i++){
		if(a[i-1]>a[i]+1){
			a[i-1]=a[i]+1;
		}
		if(a[i]==a[i-1]-1){
			if(a[i+1]==a[i]-1){
				i++;
			}else{
				ans++;
				a[i]=a[i]-1;
			}
		}
	}
	cout<<ans<<endl;
}

D. AB-string

题意:

给定一个长度为 \(n\) 的只包含 A 和 B 的字符串 \(s\),求 \(s\) 的所有子串中,有多少个子串是“好”的。一个字符串是“好”的当且仅当它的每个字符都至少属于一个长度大于 \(1\) 的回文子串。

思路:

考虑总子串数为 \(\frac{n(n+1)}{2}\),减去“不好”的子串数。
一个子串是“不好”的当且仅当:

  • 长度为 \(1\)(显然无法属于长度 \(>1\) 的回文),或者
  • 由两个不同字符的连续段组成,且其中一段的长度为 \(1\)

对于每一对相邻的不同字符 \(s_i \neq s_{i+1}\),设 \(L\)\(s_i\) 向左连续相同字符的个数(包含 \(i\)),\(R\)\(s_{i+1}\) 向右连续相同字符的个数(包含 \(i+1\)),则这对相邻字符会贡献 \(L + R - 1\) 个长度 \(\ge 2\) 的“不好”子串。
预处理每个位置向左/向右的连续相同长度,遍历所有相邻对累加贡献,再用总子串数减去即可。

代码

点击查看代码
int a[N];
int b[N];
string s;
void solve() {
	int n;
	cin>>n;
	cin>>s;
	int ans=(n-1)*n/2;
	a[0]=1;
	for(int i=1;i<n;i++){
		if(s[i]==s[i-1]){
			a[i]=a[i-1]+1;
		}else{
			a[i]=1;
		}
	}
	b[n-1]=1;
	for(int i=n-2;i>=0;i--){
		if(s[i]==s[i+1]){
			b[i]=b[i+1]+1;
		}else{
			b[i]=1;
		}
	}
	for(int i=1;i<n;i++){
		if(s[i]!=s[i-1]){
			ans-=a[i-1]+b[i]-1;
		}
	}
	cout<<ans<<endl;
}

E. Keyboard Purchase

题意:

给定一个长度为 \(n\) 的字符串 \(s\),仅由前 \(m\) 个小写字母组成。你需要选择一个键盘布局,即前 \(m\) 个小写字母的一个排列。定义打字的慢度为:对于字符串 \(s\) 中每一对相邻字符,它们在键盘上的位置差的绝对值之和。求最小可能的慢度。

思路:

由于 \(m\) 很小(\(\le 20\)),考虑状态压缩 DP。设 \(dp[mask]\) 表示已经将 \(mask\) 中的字母放置在键盘的前 \(|mask|\) 个位置(从左到右)时,当前的最小慢度。转移时,考虑在 \(mask\) 中加入一个新字母 \(i\),将其放在当前所有已放置字母的右边(位置为 \(|mask|\))。
新增的代价为:\(\displaystyle|mask| \times (2 \times \sum_{j \in mask} cnt[i][j] - s1[i])\),其中 \(cnt[i][j]\) 为字母 \(i\)\(j\) 在字符串中相邻的次数(无序对),\(s1[i]\) 为字母 \(i\) 与所有其他字母的 \(cnt\) 值之和。
\(\sum_{j \in mask} cnt[i][j]\) 可以通过SOSDP预处理,时间复杂度为 \(O(m \times 2^m)\)

代码

点击查看代码
string s;
int cnt[26][26];
int dp[1<<20];
int sum[22][1<<20];
int s1[22];
void solve() {
	int n,m;
	cin>>n>>m;
	cin>>s;
	for(int i=1;i<n;i++){
		cnt[s[i]-'a'][s[i-1]-'a']++;
		cnt[s[i-1]-'a'][s[i]-'a']++;
	}
	int S=1<<m;
	for (int i = 0; i < m; i++) {
		for (int j = 1; j < S; j++) {
			int lb = j & -j;
			int k = __builtin_ctz(lb);
			sum[i][j] = sum[i][j ^ lb] + cnt[i][k];
		}
	}
	for(int i=0;i<m;i++){
		for(int j=0;j<m;j++){
			if(i!=j){
				s1[i]+=cnt[i][j];
			}
		}
	}
	dp[0] = 0;
	for (int j = 1; j < S; j++) {
		dp[j]=1e18;
	}
	for (int j = 1; j < S; j++) {
		for (int i = 0; i < m; i++) {
			if (j >> i & 1) {
				int ct = __builtin_popcount(j ^ (1 << i));
				int ndp = dp[j ^ (1 << i)] + ct * (2 * sum[i][j ^ (1 << i)] - s1[i]);
				dp[j] = min(dp[j], ndp);
			}
		}
	}
	cout << dp[S - 1] << endl;
}
posted @ 2026-02-03 12:00  ptlks  阅读(0)  评论(0)    收藏  举报