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(待补)