Codeforces Round #692 (Div. 2, based on Technocup 2021 Elimination Round 3)

本来以为会掉分,结果竟然涨了

A - In-game Chat

签到题

给定只含')'和小写字母的字符串,问结尾的')'数量是否比剩余的字符数量多

签到题,直接从后往前扫就行

B - Fair Numbers

思维

求大于等于\(n\)的能被每一位数整除的最小的数

如果一个数能被\(a,b\)整除,那么他一定也能被\(lcm(a,b)\)整除

\(1,2,...,9\)的最小公倍数是\(2520\),最小的公平数一定小于等于\(1,2,...,9\)都出现的公平数,所以公平数和\(n\)直接差距很小,直接枚举即可

这题浪费了比较长时间,一开始想了个假贪心,后来忽略除数为\(0\)的情况了

    while (x) {
        if (x % 10 == 0) { x /= 10; continue; }//!!!
        if (y % (x % 10)) return 0;
        x /= 10;
   	}

C - Peaceful Rooks

思维+图论

给定\(n*n\)的网格,有\(m\)个马\((m < n)\),每个马能垂直或者水平移动任意距离,马之间不能互相攻击

问最少多少步能把所有马都移动到主对角线\((i,i)\)

比赛时只想如果相互攻击,就可以想把一个移动到空闲位置,然后剩下的那个移动到对角线,空闲位置的再移动,但是并没有想到怎么实现

我们不妨建立图看一下,位置(x,y)对应图中边(x,y),根据题目的性质,每个点最多只有一条出边

如果出现了环,就说明这几个能互相攻击,我们都需要多一步去把其中一个先移动到空闲位置,也就是说环的步数比正常路径多了\(1\)

所以本题只需要建图找环就行了。

参考代码,用的并查集。

int T, n, m;
int f[N];
int find(int x){
	if(f[x] == x) return x;
	else return f[x] = find(f[x]);
}
int main()
{
	T = read();
	while(T--){
		int ans = 0;
		n = read(), m = read();
		for(int i = 1; i <= n; ++i) f[i] = i;
		for(int i = 1; i <= m; ++i){
			int x = read(), y = read();
			if(x == y) continue;
			ans++;
			int fx = find(x), fy = find(y);
			if(fx == fy) ans++;//环 
			else f[fx] = fy;
		}
		cout << ans << '\n';
	}
	return 0;
}

本题提供了一中二维坐标系建图的思路

以前我只遇到过一次这种二维坐标系建图的题目,这一次完全没想到,要长点记性

D - Grime Zoo

思维+贪心

给你一个由\(‘0’\)\(‘1’\)\(‘?‘\)组成,其中有一个\(01\)子序列就会产生\(x\)点愤怒值。每有一个\(10\)子序列就会产生\(y\)点愤怒值。其中问号\(’?'\)可以变成\(0\)或者是\(1\)。问愤怒值的和最小为多少。

这题给我的第一感觉是\(dp\),可惜我只想到一个空间复杂度是\(n^2\)\(dp\),数组开不下,竟然没想到贪心.....

接下来就说一下贪心的思路吧,这题应该有很多思路,不过我就看懂了贪心(最好想)

显然\(x\)\(y\)哪个大,我们就尽量要哪个多,详细地来说就是:

如果\(x>y\),我们就让\(10\)尽量多,就把\(1\)尽量往前填。

如果\(x<y\),我们就让\(01\)尽量多,就把\(1\)尽量往后填。

具体实现是可以通过前缀和和后缀和加速。

代码参考网上的题解(我不会写

const int N = 1e5 + 2020;
char str[N];
int x, y, st[N][3], ed[N][3];
vector<int> g;

signed main()
{
   scanf("%s", str + 1);
   scanf("%lld%lld", &x, &y);
   int len = strlen(str + 1);
   for(int i = 1; i <= len; ++i){
   	st[i][0] = st[i - 1][0];
   	st[i][1] = st[i - 1][1];
   	st[i][2] = st[i - 1][2];
   	if(str[i] == '0') st[i][0]++;
   	else if(str[i] == '1') st[i][1]++;
   	else{
   		st[i][2]++;	
   		g.push_back(i);
   	} 
   }
   for(int i = len; i >= 1; --i){
   	ed[i][0] = ed[i + 1][0];
   	ed[i][1] = ed[i + 1][1];
   	ed[i][2] = ed[i + 1][2];
   	if(str[i] == '0') ed[i][0]++;
   	else if(str[i] == '1') ed[i][1]++;
   	else ed[i][2]++;
   }
   int num0 = 0, num1 = 0;
   int ans = 0;
   for(int i = 1; i <= len; ++i){
   	if(str[i] == '0'){
   		ans += num1 * y;
   		num0++;
   	}
   	else {//此处默认?是1,注意后面把?修改成0时要先减去此时多算的部分
   		ans += num0 * x;
   		num1++;
   	}
   }
   int res = ans;
   if(x < y) //10更大
   {
   	for(int i = 0; i < g.size(); ++i){
   		int pos = g[i];
   		res = res - (st[pos - 1][0] + st[pos - 1][2]) * x - ed[pos + 1][0] * y;
   		res = res + st[pos - 1][1] * y + (ed[pos + 1][1] + ed[pos + 1][2]) * x;
   		ans = min(ans, res);
   	}
   } 
   else {
   	for(int i = g.size() - 1; i >= 0; --i){
   		int pos = g[i];
   		res = res - st[pos - 1][0] * x - (ed[pos + 1][0] + ed[pos + 1][2]) * y;
   		res = res + (st[pos - 1][1] + st[pos - 1][2]) * y + ed[pos + 1][1] * x;
   		ans = min(res, ans); 
   	}
   }
   cout << ans;
   return 0;
}

注意题目是要求怒气值最小,不是最大(大雾

E - Poman Numbers

贪心

给出一个长度为 \(n\) 的字符串,每个字符串实质上代表一个数字\(2^{pos(i)}\),现在需要寻找一种递归顺序,使得每个位置非负即正,递归规则如下:

\[f(S)=-f(S[1, m])+f(S[m+1,|S|]) \]

其中\(m\)\([l,r]\)中任意的一个位置,且每一步的\(m\)都可以独立选择

问能否公国某种递归顺序,使得整个序列之和为给定的值\(sum\)

结论:\(n\)个位置的符号一定为正,第\(n−1\) 个位置的符号一定为负,其余 \(n − 2\) 个位置的符号随意

设最终我们得到的符号是\(a_1,a_2,a_3,...,a_n\)

  • \(n=2\)时,\(a_1=-1,a_2=1\)

  • \(n>2\)

    • 假设要使得左边第一个是\(-1\),递归处理\((-a_1)(a_2,a_3,...,a_n)\)即可
    • 假设要使得左边第一个是\(1\),找到从左边开始的第一个非负数记为\(i\),满足\(a_{i-1}=1,a_i=-1\)则递归子问题\((-a_1,-a_2,...,-a_{i-1},-a_i)(a_{i+1},...,a_{n})\)\(a_i\)最后一定是\(1\),且\(a_1.a_2,...a_{i-1}\)都能递归成正数

目前,问题就变为了前\(n-2\)个数得到剩余的数\(X\)(减去最后两个数),贪心选取即可。

const int N = 1e6 + 2020;
int f[N], n, cnt[N], T;
char s[N];

signed main()
{
	f[0] = 1;
	for(int i = 1; i <= 26; ++i) f[i] = f[i - 1] * 2;
	n = read(), T = read();
	scanf("%s", s + 1);
	T -= f[s[n] - 'a'] - f[s[n - 1] - 'a'];
	for(int i = 1; i <= n - 2; ++i) T += f[s[i] - 'a'], cnt[s[i] - 'a']++;
	for(int i = 25; i >= 0; --i) {
		while(cnt[i] && T >= 2 * f[i]){
			T -= 2 * f[i];
			cnt[i]--;
		}
	}
	if(T) cout << "NO";
	else cout << "YES";
	return 0;
}

F - The Thorny Path

贪心

又是贪心,更难了.....

posted @ 2020-12-23 21:36  pyyyyyy  阅读(106)  评论(1编辑  收藏  举报