Codeforces Round #646 (Div. 2) A-D E

A.Odd Selection

题意:问n个数里面选x个数总和是否能组成奇数,任何偶数之和都是偶数,所以我们只需要让奇数的个数是奇数,奇数个数为cnt1,偶数cnt2,对于1,3,5,7…<=cnt1只要偶数能满足 cnt2>=(x-i)即可,注意一下只有奇数的情况要特判下

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;

int main()
{
   int t,x,n;
   scanf("%d",&t);
   while(t--)
   {
      bool flag=0;
      scanf("%d%d",&n,&x);
      int len1=0,len2=0;
      int a;
      for(int i=1;i<=n;++i)
      {
        scanf("%d",&a);
        if(a%2==0)
            len1++;
        else
            len2++;
      } 
      if(len2==0)
          puts("No");
      else if(len1==0)
      {
         if(x%2==0)
            puts("No");
         else
            puts("Yes"); 
      }
      else
      {
           for(int i=1;i<=len2;i+=2)
          {
              if(len1>=(x-i)) 
             {
                flag=1;
                break; 
             }
          }
           if(flag)
               puts("Yes");
           else
               puts("No");
      }
   }
   return 0; 
}

B.Subsequence Hate

题意:给你一个01串,每次可以反转一个0或者1,问你怎么可以做到没有010和101的子序列下的最小反转次数

题解:显然最后的答案只可能是0000111111或者1111100000,(全0全1也是这两种特殊情况之一),所以统计0和1的前后缀的个数,暴力枚举每个点,选择使其变为这两种情况的最小步数,这样保证枚举了答案的所有情况,所以一定是最优

C.Game On Leaves

题意:博弈论水题,给一颗树,有一个特殊节点,AB轮流拿,A先拿,每次必须取完叶子节点才可以取上层节点,问谁能赢

题解:全场最水的题,对于特殊的点,当度数<=1时显然A赢,否则就把它当成树的根,由于每次必须取完叶子节点才可以取上层节点,所以下层的节点的排列毫无关系,只要判断在取到根节点前的节点数即可,所以答案就是(n-1)%2==0时B赢,否则A赢

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
int deg;
int main()
{
   int t,n,k;
   scanf("%d",&t);
   while(t--)
   {
      scanf("%d%d",&n,&k);
      deg=0;
      for(int i=1;i<n;++i)
      {
         int a,b;
         scanf("%d%d",&a,&b);
         if(a==k||b==k)deg++;
      }
      if(deg<=1)
          puts("Ayush");
      else
      {
         if((n-1)%2==0)
            puts("Ashish");
         else
            puts("Ayush"); 
      }
   }
   return 0; 
}

D.Guess The Maximums

题意:
通过查询构造出答案要求的密码,每次查询可以询问数组A中指定的集合的最大值,最多可以查询12次,我们要构造的密码序列,对于一个密码Pi, Pi为除了Si这个集合中索引对应的数组A中的数字的最大值,Si是k个互不独立的集合。

题解:
二分+交互,首先看到log2(1000)<10,12次,显然是要2分或者倍增,画出维恩图后发现答案只可能全都是最大值或者只有1个不是最大值,所以先一次查询问出最大值,然后二分10次以内问出最大值的下标,再扫一遍看看集合里面有没有这个下标,没有就是最大值,否则就再询问最后一次输出该集合的补集中的最大值


#include <bits/stdc++.h>
 
using namespace std;
const int N = 1005;
vector<int> v[N];
int res[N];
 
int query(int x)
{
	printf("? ");
	printf("%d ", x);
	for (int i = 1; i <= x; ++i)
	{
		printf("%d ", i);
	}
	printf("\n");
	fflush(stdout);
	int res;
	scanf("%d", &res);
	return res;
}
 
int main()
{
	int t;
	scanf("%d", &t);
	while (t--)
	{
		int n, k;
		scanf("%d%d", &n, &k);
		for (int i = 1; i <= k; ++i)
		{
			v[i].clear();
			int sz;//集合的大小
			scanf("%d", &sz);
			int u;
			for (int j = 1; j <= sz; ++j)
			{
				scanf("%d", &u);
				v[i].push_back(u);
			}
		}
		int mx = query(n);
		int l = 1, r = n;
		int pos = 1;
		while (l < r) {
			int mid = l + r >> 1;
			if (query(mid) == mx) 
				r = mid;
			else 
				l = mid + 1;
		}
		pos = l;
		for (int i = 1; i <= k; ++i)
		{
			bool flag = false;
			map<int, int> mp;
			for (int j = 0; j < v[i].size(); ++j)
			{
				if (v[i][j] == pos)
					flag = true;
				mp[v[i][j]] = 1;//这个位置是不行的
			}
			if (!flag)
				res[i] = mx;
			else
			{
				printf("? ");
				printf("%d ", n - v[i].size());
				for (int i = 1; i <= n; ++i)
				{
					if (mp[i]) continue;
					printf("%d ", i);
				}
				printf("\n");
				fflush(stdout);
				int p = 0;
				scanf("%d", &p);
				res[i] = p;
			}
		}
		printf("! ");
		for (int i = 1; i <= k; ++i)
			printf("%d ", res[i]);
		printf("\n");
		fflush(stdout);
		char s[20];
		scanf("%s", s);
	}
	return 0;
}

E.Tree Shuffling

题意:给你一颗以节点1为根的树,每个树上有一个二进制位,有当前位和目标的位,每次你可以选择某个节点的子树中的任意节点,花费a[i],任意调转它们二进制位的顺序,问最少花费使得全部节点符合期望位置,假如无法做到,就输出-1

题解:
显然可以想到一个性质,对于一个节点来说,选择它可以完成的事,选择它的祖先节点也可以做到,所以我们考虑贪心,对于一个节点,假如它的祖先中有更优的,就交由它的最优祖先来做,否则,就自己来做排列,所以自顶向下dfs一次,记录每个节点子树需要反转的01和10的个数以及祖先的最优花费,回溯的时候判断是否要在当前节点排列,最后看下根节点的01和10数是否为0即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn=2e5+10;

int sum01[maxn],sum10[maxn],INF=0x7FFFFFFF,a[maxn],b[maxn],c[maxn];
ll ans=0;
vector<int>G[maxn];
void dfs(int now,int fa,int mn)
{
   sum01[now]=(b[now]==0&&c[now]==1);
   sum10[now]=(b[now]==1&&c[now]==0);
   for(auto&v:G[now])
   {
      if(v==fa)continue;
      dfs(v,now,min(mn,a[now]));
      sum01[now]+=sum01[v];
      sum10[now]+=sum10[v];      
   } 
   if(a[now]<mn)//贪心的选
   {
        int cnt=min(sum01[now],sum10[now]);
        ans+=2ll*a[now]*cnt;
        sum01[now]-=cnt;
        sum10[now]-=cnt; 
   }
}
int main()
{
   int n;
   scanf("%d",&n);
   for(int i=1;i<=n;++i)
      scanf("%d%d%d",&a[i],&b[i],&c[i]);
   int a,b;
   for(int i=1;i<n;++i)
   {
       scanf("%d%d",&a,&b);
       G[a].push_back(b); 
       G[b].push_back(a);
   } 
   dfs(1,0,INF);
   if(sum01[1]!=0||sum10[1]!=0)
       puts("-1");
   else
       printf("%lld\n",ans);
   return 0; 
}

F.Guess The Maximums(待补)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值