• 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 << '
    ';
    	}
    	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

    贪心

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

  • 相关阅读:
    sql 基础--mysql 5 (4)
    The Best Strategy
    Rectangles
    Good Coins
    深搜模板
    求数的和 必须是个位数
    TC中,音乐,正弦曲线,满天星,成绩柱状图
    1,2,3的交换
    Robot's Task(机器人破解计算机)
    Asphalting Roads(判断什么时候修马路)
  • 原文地址:https://www.cnblogs.com/pyyyyyy/p/14181516.html
Copyright © 2020-2023  润新知