一万五参赛,赛时(VP)排名 80
A. Dora's Set
比较新的结论题。显然,一个合法三元组最多只能有一个偶数。每个偶数都可以跟相邻两个奇数组成合法三元组。
因此,答案为奇数个数的二分之一(下取整)。
#include<bits/stdc++.h>
using namespace std;
int T, l, r;
int main() {
scanf("%d", &T);
while(T --) {
scanf("%d%d", &l, &r);
int num2 = r / 2 - l / 2;
if(l % 2 == 0) num2 ++;
printf("%d\n", (r - l + 1 - num2) / 2);
}
return 0;
}
B. Index and Maximum Value
简单结论题。显然,每次修改不会改变最大值的地位,要么最大值跟着变小或者变大,要么最大值没有影响。
因此每次只需要判断最大值是否在变化区间内即可。
VP时看错题了,白白耽误了 15 min
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int T, n, m, a[N];
int main() {
scanf("%d", &T);
while(T --) {
int mx = 0;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++)
scanf("%d", &a[i]), mx = max(mx, a[i]);
for(int i = 1; i <= m; i ++){
char op[2]; int l, r;
scanf("%s%d%d", op, &l, &r);
if(op[0] == '+' && r >= mx && l <= mx) mx ++;
if(op[0] == '-' && r >= mx && l <= mx) mx --;
printf("%d ", mx);
}
puts("");
}
return 0;
}
C. Dora and C++
裴蜀定理。显然,根据裴蜀定理,每个数都可以加减任意次 gcd(a,b) ,且这是最小最优的数。
因此,对每个数取模,框定在 gcd(a,b) 范围内,并且从小到大依此加一次 gcd(a,b) ,动态更新答案即可。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int T, n, a, b, c[N];
int gcd(int a, int b) {
if(!b) return a;
return gcd(b, a % b);
}
int main() {
scanf("%d", &T);
while(T --) {
scanf("%d%d%d", &n, &a, &b);
int gd = gcd(a, b);
for(int i = 1; i <= n; i ++) {
scanf("%d", &c[i]);
c[i] %= gd;
}
sort(c + 1, c + n + 1);
int ans = c[n] - c[1];
for(int i = 2; i <= n; i ++)
ans = min(ans, c[i - 1] - c[i] + gd);
printf("%d\n", ans);
}
return 0;
}
D. Iris and Game on the Tree
怎么还是结论题。。。
手玩一下可以发现,一个叶子节点代表的 01 串的权值只能为 0 或者 1 ,且只跟首尾字符是否一致有关,与中间无关。
分类讨论。
若根节点可以填,优先填在叶子出现最多的字符,若一样多,先填中间无关紧要的(此时后手填根节点更优)。
若根节点固定,则抢占叶子节点的有利字符即可。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int T, n, w[N], cnt[4];
vector<int> g[N];
char s[N];
void dfs(int x, int fa) {
bool is_leaf = true;
for(auto y: g[x]) {
if(y == fa) continue;
dfs(y, x);
is_leaf = false;
}
if(is_leaf) cnt[w[x]] ++;
else if(x != 1 && w[x] == 2) cnt[3] ++;
}
int main() {
scanf("%d", &T);
while(T --) {
scanf("%d", &n);
for(int i = 1; i <= n; i ++) g[i].clear();
cnt[0] = cnt[1] = cnt[2] = cnt[3] = 0;
for(int i = 2; i <= n; i ++) {
int u, v;
scanf("%d%d", &u, &v);
g[u].push_back(v);
g[v].push_back(u);
}
scanf("%s", s + 1);
for(int i = 1; i <= n; i ++) {
if(s[i] == '0') w[i] = 0;
if(s[i] == '1') w[i] = 1;
if(s[i] == '?') w[i] = 2;
}
dfs(1, 0);
if(w[1] != 2) printf("%d\n", cnt[w[1] ^ 1] + (cnt[2] + 1) / 2);
else {
if(cnt[0] != cnt[1])
printf("%d\n", max(cnt[1], cnt[0]) + cnt[2] / 2);
else
printf("%d\n", cnt[0] + ((cnt[3] & 1) ? (cnt[2] + 1) / 2 : cnt[2] / 2));
}
}
return 0;
}
E. Iris and the Tree
没错还是结论题。。。
显然,若 dis(i,i+1) 中间有无权边,可以将这条无权边的边权设置成最大,因此我们只需要知道含无权边的路径有多少即可。
又显然,每条边会被经过两次,对于 i 节点对应边,分别是 dis(i-1,i) 和 dis(mx,mx+1) ,mx 是 i 节点所在子树的最大节点。
因此每次加一条边权,两条对应路径的无权边减一。计算相应答案即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
ll T, n, w, cnt, cur;
ll mx[N], dis[N], p[N];
vector<int> g[N];
void dfs(int x) {
if(x == cur + 1) {
dis[cur] = cnt;
cnt = 0; cur ++;
}
mx[x] = x;
for(auto y: g[x]) {
cnt ++;
dfs(y);
mx[x] = max(mx[x], mx[y]);
}
if(x != 1) cnt ++;
}
int main() {
scanf("%lld", &T);
while(T --) {
scanf("%lld%lld", &n, &w);
for(int i = 1; i <= n; i ++)
g[i].clear();
for(int i = 2; i <= n; i ++) {
scanf("%d", &p[i]);
g[p[i]].push_back(i);
}
cur = 1; cnt = 0;
dfs(1);
dis[cur] = cnt;
cnt = n;
ll sum = 0;
for(int i = 2; i <= n; i ++) {
ll x, y;
scanf("%lld%lld", &x, &y);
if(-- dis[x - 1] == 0) cnt --;
if(-- dis[mx[x]] == 0) cnt --;
sum += y;
printf("%lld ", sum * 2 + cnt * (w - sum));
}
puts("");
}
return 0;
}