Codeforces Round #646 (Div. 2)

这次CF有事没打,后期补了ABCE

传送门

A. Odd Selection

总思路: 循环找出奇数个奇数,判断是否存在符合的情况

解题思路:
  • 首先要求 x 个数,他们的数之和为奇数
  • 这个题有O(1) 做法,不过相对麻烦些,这里写一下稍微思维简单些的O(n)做法
  • 首先输入,我们求出 有 a 个奇数, b 个偶数
  • 给予的 x 的个数,我们肯定会选出奇数个奇数,剩下的选偶数就可以,所以我们根据这个规律直接循环。
for (int i = 1; i <= x; i += 2){
    if (a >= i && b >= x - i){
         puts("YES");
         f = 1;
         break;
    }
}
  • 如果中间有符合的情况直接输出即可
代码:
#include 
#include 
#include 
#include 

using namespace std;

typedef long long ll;

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        int n, x, y;
        int a = 0, b = 0;
        scanf("%d%d",&n,&x);
        for (int i = 0; i < n; i++){
            scanf("%d",&y);
            if (y % 2 == 1){
                a ++;
            }
            else{
                b ++;
            }
        }
        int f = 0;
        for (int i = 1; i <= x; i += 2){
            if (a >= i && b >= x - i){
                puts("YES");
                f = 1;
                break;
            }
        }
        if (!f) puts("NO");
    }
    return 0;
}


B. Subsequence Hate

总思路 : 利用前缀和记录没点前面有多少 0 ,后面有多少0,然后排着遍历输出最小的费用 (可以用多少个0推出有多少个1)

解题思路:
  • 给予01字符串,求出最小的费用
  • 其实符合条件的有四种,第一种:全是0 , 第二种:全是1 , 第三种 : 前面都是1 后面都是 0, 第四种 : 前面是 0 后面都是1, 这个应该很好理解。
  • 然后前面两种费用的花费很容易求出,利用前缀和0的数目即可,关键是后面两种。
  • 这里就用第三个举例,第四个是相通的道理,我们用前缀和记录当前点前面有多少个0,后面有多少个0,然后如果是第三种请求,让前面都是1,那么就是让前面的0变成1,花费就是当期的前缀和0的数量,然后将后面的1变为0,也就是向后的数目(n - i - res[i]),res数组记录当前位置后方有多少个0,ans记录前面有多少个0(包含自身);
		for (int i = 1; i <= n; i ++){
            if (st[i] == '0'){ 
                ans[i] = ans[i - 1] + 1;
            }
            else{
                ans[i] = ans[i - 1];
            }
        }
        for (int i = n; i >= 1; i --){
            if (st[i] == '0'){
                res[i - 1] = res[i] + 1;
            }
            else{
                res[i - 1] = res[i];
            }
        }
  • 然后这样排着遍历,每次求2种情况(第三种、第四种),最终取最小值即可。
		int num = min(ans[n], n - ans[n]);

        for(int i = 1; i <= n; i++){
            int x = ans[i] + (n - i - res[i]);
            int y = i - ans[i] + res[i];
            num = min({x,y,num});
        }
代码:
#include 
#include 
#include 
#include 
#include 

using namespace std;

int ans[1010],res[1010];

char st[1010];

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%s",st + 1);
        int n = strlen(st + 1);
        
        memset(ans,0,sizeof ans);
        memset(res,0,sizeof res);
        for (int i = 1; i <= n; i ++){
            if (st[i] == '0'){ // 有多少 0
                ans[i] = ans[i - 1] + 1;
            }
            else{
                ans[i] = ans[i - 1];
            }
        }
        for (int i = n; i >= 1; i --){
            if (st[i] == '0'){
                res[i - 1] = res[i] + 1;
            }
            else{
                res[i - 1] = res[i];
            }
        }
        // printf("%d\n",n);
        int num = min(ans[n], n - ans[n]);

        for(int i = 1; i <= n; i++){
            int x = ans[i] + (n - i - res[i]);
            int y = i - ans[i] + res[i];
            num = min({x,y,num});
        }

        printf("%d\n",num);
    }
    return 0;
}


C. Game On Leaves

主要思路: 思维博弈,无根树将x点提取为根,然后找谁能先删除即可,最后肯定是剩了2个点,谁能先下手谁就赢。

解题思路:
  • 主思路差不多只是了解一下,这里详细讲一下
  • 最后能赢的情况肯定是最后剩2个点,然后下一次下手的人获胜,所以可以想到n - 2,这些是需要拿走的点,然后2个人,Au先出手,Al后出手,所以周期是2,那么我们判断 (n - 2) % 2 的取值即可,如果为0,那么下次出手的肯定是Ay,所以Ay获胜,如果是1,那么下次出手的肯定是As,所以As获胜,这里可以自己画个图理解下,所以n - 2 取模 和 n取模的结果相同,就不用 -2了,-2是为了更好的理解
  • 然后还有一个点就是如果他的度为1,那么说明他是一个叶子结点,先手就可以取胜
  • 还有一种情况是n == 1, 也可以先手取胜。
代码:
#include 
#include 
#include 
#include 

using namespace std;

int d[1010];

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        memset(d,0,sizeof d);
        int n, x;
        scanf("%d%d",&n,&x);
        for (int i = 1; i <= n - 1; i++){
            int u, v;
            scanf("%d%d",&u,&v);
            d[u]++,d[v]++;
        }
        if (n == 1 || d[x] == 1){
            puts("Ayush");
            continue;
        }
        if (n % 2 == 0){
            puts("Ayush");
        }
        else{
            puts("Ashish");
        }
    }
    return 0;
}


E. Tree Shuffling

主要思路,贪心 + dfs,不太好理解,每条子树的变换点都会取用这条树上的最小值点去变换,dfs记录 0 1 的个数,然后从低向上去贪心计算

解题思路:
  • 这题不太好理解,可能会说的不太明白,先说下题意:
  • 给予一棵树 n个点,n - 1条边,根节点为1,然后选取子树,然后这个子树中任意两个点可以相互交换 b 的值,最终目的是都要让b的值和自身c的值相同,然后花费的话是选取k个点进行随意交换(选取这个子树的k个点),然后所花费的费用是 k * a[u] 这个a[u] 就是这个子树的最小花费数额,画个图解释一下:
  • 其中我们选择橙色的为子树,然后按常理说最下面的蓝色点是他们的根节点,可以按照他的花费进行转移,但是这个子树同时也是第一个蓝色点和第二个蓝色点的子树(就和离散中的哈斯图那种选取集合的A B 的那种感觉差不多),所以他的最小交换值点就是这些蓝色点的最小值,而且交换的话肯定是两个交换(也就是k = 2)的时候是最划算的(偶数的都可以推到2上,道理是一样的,想一下就OK)。
    Codeforces Round #646 (Div. 2)_第1张图片
  • 然后根据这个贪心的思想,(这里我们0代表想让0变为1的点,1代表想让1变为0的点)我们每次去求每个点为根节点他的子树中节点的0 1的数目,我们用数组cnt[u][0] 计算当前点为根节点子树中点为0的数目,cnt[u][1]计算当前点为根节点子树中点为1的数目,然后用cnt[v][0],cnt[v][1]代表他的子节点的,然后对子节点的cnt[v][0],cnt[v][1]做差,这里计算出来的是他们上一次没有转换完的0,1数目,这次继续进行转化,然后用a0,a1 存起来(累加起来)
  • 然后我们根据a0 ,a1 的值,并对当前节点进行判断是0还是1,进行累加,然后取min(a1,a0),(这里取最小值的原因是加入有3个1,2个0,他们互相转换,顶多只能转换2个,因为只有2个0),然后 min(a1,a0) * 2 * a[u] 加到ans上即可 (ans是总的花费,2是k,代表两个两个进行互换,a[u]是前面所说的交换点的最小花费)
  • 然后dfs求出最终的ans即可,我们最后要判断cnt[1][0],cnt[1][1]是否相同,如果相同可以成立,如果不相同就代表0 1 的数目不是相同的,那么就输出-1.
代码:
#include 
#include 
#include 
#include 

using namespace std;

typedef long long ll;

const int N = 200010, M = 400010;

int h[N], ne[M], e[M], a[N], b[N], c[N], idx;
int cnt[N][2];

ll ans;

void add(int x, int y){
	e[idx] = y, ne[idx] = h[x], h[x] = idx++;
}

void dfs(int u, int fa){
	if (fa) a[u] = min(a[u],a[fa]);
	int f = -1;
	if (b[u] == 0 && c[u]) f = 0;
	if (b[u] && c[u] == 0) f = 1;
	int c0 = 0, c1 = 0;
	for (int i = h[u] ; i != -1; i = ne[i]){
		int j = e[i];
		if (j != fa){
			dfs(j,u);
			cnt[u][0] += cnt[j][0];
			cnt[u][1] += cnt[j][1];
			if (cnt[j][0] > cnt[j][1]) c0 += cnt[j][0] - cnt[j][1];
			else c1 += cnt[j][1] - cnt[j][0];
		}
	}
	if (f == 0) cnt[u][0] ++, c0 ++;
	if (f == 1) cnt[u][1] ++, c1 ++;
	ans += 2ll * a[u] * min(c0,c1);
}

int main(){
	memset(h,-1,sizeof h);
	int n;
	scanf("%d",&n);
	for (int i = 1; i <= n; i++){
		scanf("%d%d%d",&a[i],&b[i],&c[i]);
	}
    
    for (int i = 1; i < n ; i++){
    	int x, y;
    	scanf("%d%d",&x,&y);
    	add(x, y), add(y, x);
    }

    dfs(1,0);

    if (cnt[1][0] != cnt[1][1]) ans = -1;
    printf("%lld\n",ans);
    return 0;
}

你可能感兴趣的:(CodeForces,思维,DFS)