康复场1。
https://codeforces.com/contest/1363
A - Odd Selection
题意:问是否能在给出的 \(n\) 个数中选恰好 \(x\) 个数,使得他们的和为奇数。
题解:必须选择奇数个奇数,然后不用思考这么复杂,枚举选择 \([1,x]\) 个奇数是否可行,即可。
int n, x;
void TestCase() {
scanf("%d%d", &n, &x);
int odd = 0, even = 0;
for(int i = 1; i <= n; ++i) {
int ai;
scanf("%d", &ai);
if(ai % 2 == 1) {
++odd;
} else {
++even;
}
}
bool suc = 0;
for(int i = 1; i <= x; i += 2) {
if(i > odd)
break;
if(x - i > even)
continue;
suc = 1;
break;
}
if(suc) {
puts("Yes");
} else {
puts("No");
}
return;
}
想预处理之后 \(O(1)\) 来做,就要先使用(不超过x的)奇数个奇数,然后尽可能使用偶数。最后还要验证这些和确实是奇数才行(有可能使用了)
B - Subsequence Hate
题意:给一个01串,每次操作可以翻转一个位置,求至少多少次操作才能使得给出的01串中不含子序列"010"也不含子序列"101"。
题解:构造出的串必须是先0后1或者先1后0,搞个前缀和和后缀和统计就行。
int n;
char s[1005];
void TestCase() {
scanf("%s", s + 1);
n = strlen(s + 1);
int sum0 = 0;
int sum1 = 0;
for(int i = 1; i <= n; ++i) {
sum0 += (s[i] == '0');
sum1 += (s[i] == '1');
}
int ans = sum0;
int cnt0 = 0;
int cnt1 = 0;
for(int i = 1; i <= n; ++i) {
sum0 -= (s[i] == '0');
sum1 -= (s[i] == '1');
cnt0 += (s[i] == '0');
cnt1 += (s[i] == '1');
ans = min(ans, cnt0 + sum1);
ans = min(ans, cnt1 + sum0);
}
printf("%d\n", ans);
return;
}
C - Game On Leaves
题意:给出一棵 \(n\) 个点的无根树和一个节点 \(x\) ,两个人玩游戏,轮流操作。每次操作可以选择一个叶子(度数不超过1的节点)去除,谁去除了节点 \(x\) 谁就获胜,问最优策略下谁赢。
题解:若 \(x\) 是叶子,直接去除,先手赢,否则必须要经过一个状态(因为最优策略下没有人会先把 \(x\) 变成叶子,所以总是保留至少度数为2),就是 text P - X - P
,这个状态是后手必胜,然后求出初始状态里这个状态相差的节点个数的奇偶性就可以了。
D - Guess The Maximums
题意:交互题,题意太复杂见原题。
提示:注意 \(S_i\) 的并集未必是 \([1,n]\) 。
题解:看到这个12,大概都会往折半去想,有一个办法就是先用1次确定全集的最大值是多少,然后用至多10次来确定这个最大值在哪里(最坏情况下每次询问的长度是500-250-125-63-32-16-8-4-2-1),然后知道最大值的位置之后,找出最大值所在的 \(S_i\) (假如有的话),然后把其他的合并再询问最后一次。
int n, k;
int color[1005];
int maxnum, maxidx;
int query(int L, int R) {
printf("? %d", R - L + 1);
for(int i = L; i <= R; ++i) {
printf(" %d", i);
}
printf("\n");
fflush(stdout);
int x;
scanf("%d", &x);
assert(1 <= x && x <= n);
return x;
}
bool check(int L, int R) {
return query(L, R) == maxnum;
}
vector tmp;
void TestCase() {
scanf("%d%d", &n, &k);
memset(color, -1, sizeof(color));
for(int i = 1; i <= k; ++i) {
int s;
scanf("%d", &s);
for(int j = 1; j <= s; ++j) {
int x;
scanf("%d", &x);
color[x] = i;
}
}
maxnum = query(1, n);
int L = 1, R = n, M;
while(1) {
M = (L + R) / 2;
if(L == M) {
if(check(L, M)) {
maxidx = L;
} else {
maxidx = R;
}
break;
}
if(check(L, M)) {
R = M;
} else {
L = M + 1;
}
}
tmp.clear();
for(int i = 1; i <= n; ++i) {
if(color[i] != color[maxidx]) {
tmp.push_back(i);
}
}
if(color[maxidx] == -1) {
printf("!");
for(int i = 1; i <= k; ++i) {
printf(" %d", maxnum);
}
printf("\n");
fflush(stdout);
char s[20];
scanf("%s", s + 1);
assert(s[1] == 'C');
return;
}
printf("? %d", (int)tmp.size());
for(auto &v : tmp) {
printf(" %d", v);
}
printf("\n");
fflush(stdout);
int x;
scanf("%d", &x);
assert(1 <= x && x <= n);
printf("!");
for(int i = 1; i < color[maxidx]; ++i) {
printf(" %d", maxnum);
}
printf(" %d", x);
for(int i = color[maxidx] + 1; i <= k; ++i) {
printf(" %d", maxnum);
}
printf("\n");
fflush(stdout);
char s[20];
scanf("%s", s + 1);
assert(s[1] == 'C');
return;
}
*E - Tree Shuffling
题意:给出一棵 \(n\) 个点的有根树,根是1号点。每个节点有三个值 \(a,b,c\) , \(a\) 表示这个节点的操作代价, \(b\) 表示节点的初始状态(只能是0或1), \(c\) 表示节点的目标状态(只能是0或1)。每次操作可以选择一个节点,然后选择其子树中的任意个节点,然后把这些节点的值交换到你想要的样子,求把整棵树变成目标状态的最小代价。
题解:一开始想了一个树dp,是错的,这个树dp是 \(dp[u]\) 表示节点 \(u\) 的子树复原的最小代价,然后若不能复原则为无穷。若 \(u\) 可以复原,则 \(dp[u]=\sum\limits_{v\in son(u)}min(dp[v],a[u]*dif[v])\) ,其中 \(dif[v]\) 表示节点 \(v\) 中初始状态和目标状态不同的数量。后来打了一个 \(a[u]=min(a[u],a[p])\) 的补丁,还是错的。错在一棵树就算没办法复原也可以尽可能复原(利用子树中的低代价来复原大部分,只留下一种不能复原的)。
int n, x;
int a[200005];
int b[200005];
int c[200005];
vector G[200005];
int siz[200005];
int cnt01[200005];
int cnt10[200005];
ll res;
void dfs(int u, int p, int ap) {
a[u] = min(a[u], ap);
siz[u] = 1;
cnt01[u] = (b[u] == 0 && c[u] == 1);
cnt10[u] = (b[u] == 1 && c[u] == 0);
for(auto &v : G[u]) {
if(v == p)
continue;
dfs(v, u, a[u]);
siz[u] += siz[v];
cnt01[u] += cnt01[v];
cnt10[u] += cnt10[v];
}
int tmp = min(cnt01[u], cnt10[u]);
cnt01[u] -= tmp;
cnt10[u] -= tmp;
res += 2ll * tmp * a[u];
return;
}
void TestCase() {
scanf("%d", &n);
for(int i = 1; i <= n; ++i) {
G[i].clear();
scanf("%d%d%d", &a[i], &b[i], &c[i]);
}
for(int i = 1; i <= n - 1; ++i) {
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
res = 0;
dfs(1, 0, INF);
if(cnt01[1] || cnt10[1])
res = -1ll;
printf("%lld\n", res);
return;
}