Berserk And Fireball
出处:CF1380D
题目大意:一排有 $n$ 个士兵,每个士兵的力量是 $a_i$ ,所有的力量都互不相同。
有两种法术可以释放:
- 火球术:使用 $x$ 法力值然后摧毁 $k$ 个连续士兵。
- 狂乱:使用 $y$ 法力值然后选择两个连续士兵,力量较大的士兵摧毁力量较小的士兵。
现在给出初始的 $a_1,\ldots,a_n$ ,问是否能将它转变为 $b_1,\ldots,b_n$ ,若能请给出释放的最少法力值,若不能请输出 $-1$ 。
数据范围: $1 \leq n,m \leq 2 \cdot 10^5,1 \leq x,y \leq 10^9,1 \leq k \leq n,1 \leq a_i \leq n,1 \leq b_i \leq n$ 。
思路:首先非常显然的是验证 $b$ 是否为 $a$ 的子序列,如果不是则直接输出 $-1$ ,如果是的话找出它在 $a$ 中对应的点并分割。
然后,就可以对 $a$ 中分割的段进行处理,注意本题一看就是一个贪心大模拟,我们要仔细分类讨论:
- 首先入股段长大于等于 $k$ ,可以使用狂乱到最近的 $k$ 倍数,然后再用多次火球术。
- 如果当前段内最大值小于端点最大值,那么可以直接全用狂乱。
- 如果当前段内最大值大于端点最大值,但是段长小于 $k$ ,那么直接输出 $-1$ 。
- 如果当前段内最大值大于端点最大值,但是段长大于等于 $k$ ,那么可以直接让他们使用狂乱到 $k$ 后再用一次火球术(这里用了贪心,因为尽量大地用火球术的版本前文已经讨论过了,这里看一下尽量小的)。
由于每个段互相独立,因此加起来即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
void solve() {
int n, m;
cin >> n >> m;
ll x, y, k;
cin >> x >> k >> y;
vi a(n);
rep(i, 0, n - 1) cin >> a[i];
vi b(m);
rep(i, 0, m - 1) cin >> b[i];
if (n == m) {
bool flag = true;
rep(i, 0, n - 1) {
if (a[i] != b[i]) {
flag = false;
break;
}
}
if (flag)
cout << 0 << endl;
else
cout << -1 << endl;
return;
}
map<int, int> ma;
rep(i, 0, n - 1) ma[a[i]] = i;
rep(i, 0, m - 1) {
if (!ma.count(b[i])) {
cout << -1 << endl;
return;
}
b[i] = ma[b[i]];
}
rep(i, 0, m - 2) {
if (b[i] >= b[i + 1]) {
cout << -1 << endl;
return;
}
}
SegmentTree tree(a);
ll ans = 0;
rep(i, 0, m - 1) {
if (i == 0 && b[i] != 0) {
ll tem4 = LLONG_MAX;
int tem3 = b[i];
int tem2 = tree.query(0, b[i] - 1);
if (tem3 < k && tem2 > a[b[i]]) {
cout << -1 << endl;
return;
}
if (tem2 < a[b[i]]) {
tem4 = min(tem4, 1LL * tem3 * y);
} else {
int tem5 = tem3 - k;
tem4 = min(tem4, 1LL * tem5 * y + 1LL * x);
}
if (tem3 >= k) {
int tem5 = tem3 % k;
tem4 = min(tem4, 1LL * y * tem5 + 1LL * x * (tem3 / k));
}
ans += tem4;
}
if (i == m - 1) {
ll tem4 = LLONG_MAX;
int tem3 = n - 1 - b[i];
if (tem3 == 0) continue;
int tem2 = tree.query(b[i] + 1, n - 1);
if (tem3 < k && tem2 > a[b[i]]) {
cout << -1 << endl;
return;
}
if (tem2 < a[b[i]]) {
tem4 = min(tem4, 1LL * tem3 * y);
} else {
int tem5 = tem3 - k;
tem4 = min(tem4, 1LL * tem5 * y + 1LL * x);
}
if (tem3 >= k) {
int tem5 = tem3 % k;
tem4 = min(tem4, 1LL * y * tem5 + 1LL * x * (tem3 / k));
}
ans += tem4;
} else {
int tem = b[i + 1];
if (tem == b[i] + 1) continue;
int tem2 = tree.query(b[i] + 1, tem - 1);
int tem3 = tem - b[i] - 1;
ll tem4 = LLONG_MAX;
if (tem3 < k && tem2 > max(a[b[i]], a[tem])) {
cout << -1 << endl;
return;
}
if (tem2 < max(a[b[i]], a[tem])) {
tem4 = min(tem4, 1LL * tem3 * y);
} else {
int tem5 = tem3 - k;
tem4 = min(tem4, 1LL * y * tem5 + 1LL * x);
}
if (tem3 >= k) {
int tem5 = tem3 % k;
tem4 = min(tem4, 1LL * y * tem5 + 1LL * x * (tem3 / k));
}
ans += tem4;
}
}
cout << ans << endl;
return;
}
|
Bitwise Queries(Easy)
出处:CF1451E1
题目大意:交互题,给定一个隐藏序列 $a$ ,现在要求在 $n+2$ 次操作内找出 $a$ 中所有元素的值,每次可以任意选择两个不同 $i,j$ ,求出 $a_i \ xor \ a_j$ , $a_i \ or \ a_j$ 或者 $a_i \ and \ a_j$ 。
数据范围: $4 \leq n \leq 2^{16}$ , $n$ 是 $2$ 的次幂。
思路:事实上好像在Easy Version中 $n$ 是 $2$ 的次幂完全没有用诶。
好的,看到题目给出了三种位运算形式,显然会回想起一个经典结论,那就是或运算可以用与运算跟异或运算表示出来,然后还有 $a+b=a \ and \ b \times 2+a \ xor \ b$ 。
于是,看到本题,可以想到先求出一个数,然后跟后续所有的数作异或运算。
然而,怎么求出一个数,注意到只给出两个数的三种运算是无法精确求出这两个数的,因此考虑三个数,有了三个数的两两之和,那么就求出来了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
int And(int l, int r) {
cout << "AND " << l << ' ' << r << '\n';
cout.flush();
int op;
cin >> op;
return op;
}
int Or(int l, int r) {
cout << "OR " << l << ' ' << r << '\n';
cout.flush();
int op;
cin >> op;
return op;
}
int Xor(int l, int r) {
cout << "XOR " << l << ' ' << r << '\n';
cout.flush();
int op;
cin >> op;
return op;
}
void report(vi& res) {
cout << "! ";
rep(i, 0, sz(res) - 1) cout << res[i] << ' ';
cout << '\n';
cout.flush();
}
void solve() {
int n;
cin >> n;
vi res(n);
int op = And(1, 2);
int op2 = Xor(1, 2);
int tem1 = op2 + 2 * op;
int op3 = And(2, 3);
int op4 = Xor(2, 3);
int op5 = And(1, 3);
int op6 = op2 ^ op4;
int tem2 = op4 + 2 * op3;
int tem3 = op6 + 2 * op5;
res[0] = (tem3 + tem1 - tem2) / 2;
res[1] = (tem1 + tem2 - tem3) / 2;
res[2] = (tem2 + tem3 - tem1) / 2;
rep(i, 3, n - 1) {
int tem = Xor(1, i + 1);
res[i] = res[0] ^ tem;
}
report(res);
return;
}
|
Finding Zero
出处:CF1634D
题目大意:交互题,给定 $a_1,\ldots,a_n$ ,其中有一个元素是 $0$ ,现在需要在至多 $2 \cdot n-2$ 次查询中找出 $a_x$ 和 $a_y$ ,其中 $a_x=0$ 或者 $a_y=0$ ,每次查询给出互不相同的 $i,j,k$ ,返回 $\max(a_i,a_j,a_k)-\min(a_i,a_j,a_k)$ 。
数据范围:$4 \leq n \leq 1000$
思路:首先很好的一个习惯是,这里 $n \geq 4$ ,代表我们一开始可以对多少个元素进行初始操作。
接着看,这道题介绍了一个很新的思路,就是动态维护当前可能的元素,因为注意到只考虑 $\max(a_i,a_j,a_k)-\min(a_i,a_j,a_k)$ 是完全无法求出到底哪个是最大值哪个是最小值的,因此考虑维护当前最大值最小值,而最后的最小值就必定是 $0$ 。
然后,就要考虑怎么处理了,还是一样的思路,如果固定两个每次拉入第三个,那么完全不好处理,于是考虑拉入第四个,每组可以查询四次,则最后可以得出极差最大的两组,它们重复的部分就是当前最大跟最小值。
如果最后还剩余一个,那么拉一个之前查过的,不是当前最大跟最小值的元素,再查一次。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
int ask(int l, int r, int m) {
cout << "? " << l << ' ' << r << ' ' << m << '\n';
cout.flush();
int op;
cin >> op;
return op;
}
void report(ll sum, ll sum2) {
cout << "! " << sum << ' ' << sum2 << '\n';
cout.flush();
}
void solve() {
int n;
cin >> n;
int op, op2;
pii ans = make_pair(1, 2);
auto upd = [&](int x, int y) {
int op1 = ask(ans.first, ans.second, x);
int op2 = ask(ans.first, ans.second, y);
int op3 = ask(ans.first, x, y);
int op4 = ask(ans.second, x, y);
int tem = max({op1, op2, op3, op4});
if (op1 == tem && op2 == tem) ans = make_pair(ans.first, ans.second);
else if (op1 == tem && op3 == tem) ans = make_pair(ans.first, x);
else if (op1 == tem && op4 == tem) ans = make_pair(ans.second, x);
else if (op2 == tem && op3 == tem) ans = make_pair(ans.first, y);
else if (op2 == tem && op4 == tem) ans = make_pair(ans.second, y);
else if (op3 == tem && op4 == tem) ans = make_pair(x, y);
return;
};
for (int i = 3; i <= n; i += 2) {
if (i == n) {
int idx = -1;
rep(j, 1, n - 1) {
if (j != ans.first && j != ans.second) {
idx = j;
break;
}
}
upd(idx, n);
} else {
upd(i, i + 1);
}
}
report(ans.first, ans.second);
return;
}
|
GCD Guess
出处:CF1665D
题目大意:交互题,需要在 $30$ 次查询内猜出一个 $1 \leq x \leq 10^9$ 的元素 $x$ ,每次可以选择 $a \not ={b}$ 的 $a,b$ ,返回 $gcd(x+a,x+b)$ 。
数据范围:$1 \leq x \leq 10^9$
思路:好的,看到这个数据范围,非常显然地,我们会想到二分,或者按位查找,但是很不幸的是,前者并没有显著的单调性,因此只能按位查找。
然后,我们要怎么查找当前位是 $0$ 还是 $1$ 呢?首先如果从高位查找的话,无法屏蔽对低位的影响,因此考虑从低位开始查找。
设当前查找的是 $i$ 位,那么固定 $b-a=2^{i+1}$ ,为了屏蔽低位影响,由于低位已经求出,那么直接加上补值使其进位,如果在第 $i$ 位也进位,那么显然地,这里需要原来的位为 $1$ ,反之为 $0$ 。
这题参考了题解。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
void ask(int l, int r) {
cout << "? " << l << ' ' << r << '\n';
cout.flush();
}
void report(ll sum) {
cout << "! " << sum << '\n';
cout.flush();
}
void solve() {
ll a, b;
ll ans = 0;
ll op;
rep(i, 0, 29) {
a = (1 << i) - ans, b = a + (1 << (i + 1));
ask(a, b);
cin >> op;
if (((op >> i) & 1) == 0) ans |= (1 << i);
}
report(ans);
return;
}
|
Small GCD
出处:CF1900D
题目大意:对于给定的 $a,b,c$ 三个数,排序后使它变为 $a \leq b \leq c$ ,然后令 $f(a,b,c)=gcd(a,b)$ 。
现在给定 $a_1,\ldots,a_n$ ,请求出 $\sum\limits_{i=1}^{n}\sum\limits_{j=i+1}^{n}\sum\limits_{k=j+1}^{n}f(a_i,a_j,a_k)$ 。
数据范围:$3 \leq n \leq 8 \cdot 10^4,1 \leq a_i \leq 10^5$
思路:由于最大值完全没有必要,因此考虑排序后枚举中间值,只需要考虑左边所有数与它的最大公因数之和。
好了,到这里就不会了,现在介绍一种求公因数和的常见方法:欧拉反演,因为注意到这个 $a_i$ 的值域,它必然可以用一些数学上的方法来做。
首先要知道,欧拉反演和莫比乌斯反演最核心的用途有以下几个:
- 把按照gcd/因子统计的题,变成按倍数或者因子枚举的题
- 把看上去 $O(n^2)$ 的题,降到 $O(V \log V)$ 量级
- 把已知前缀和式/倍数和式,反求原函数时作逆变换
欧拉反演定义:设 $f$ 是算术函数,定义
$$
\begin{aligned}
F(n)&=\sum\limits_{d \mid n} f(d),\\\\
G(n)&=\sum\limits_{d \mid n}\phi(d)f\!\left(\frac{n}{d}\right)=(\phi * f)(n).
\end{aligned}
$$
这里的 $*$ 表示狄利克雷卷积,也就是
$$
(a*b)(n)=\sum\limits_{d \mid n}a(d)b\!\left(\frac{n}{d}\right).
$$
此时,可以用下面两条式子把值函数和因子求和函数互相改写:
$$
\begin{aligned}
\sum\limits_{d \mid n}\phi(d) &= n, \\\\
\sum\limits_{d \mid \gcd(x,y)}\phi(d) &= \gcd(x,y)=\sum\limits_{d \mid x,\ d \mid y}\phi(d).
\end{aligned}
$$
本题中,可以化为
$$
\begin{aligned}
\sum\limits_{i=2}^{n}(n-i)\sum\limits_{j=1}^{i-1}\gcd(a_j,a_i)
&=\sum\limits_{i=2}^{n}\sum\limits_{j=1}^{i-1}\sum\limits_{d \mid a_i,\ d \mid a_j}\phi(d)(n-i) \\\\
&=\sum\limits_{i=2}^{n}(n-i)\sum\limits_{d \mid a_i}\phi(d)\sum\limits_{j=1}^{i-1}(d \mid a_j) \\\\
&=\sum\limits_{i=2}^{n}(n-i)\sum\limits_{d \mid a_i}\phi(d)\cdot cnt_{i-1,d}.
\end{aligned}
$$
于是就能做了。
然后我们介绍莫比乌斯反演。设 $f,g$ 是算术函数,若
$$
g(n)=\sum\limits_{d \mid n}f(d).
$$
则
$$
\begin{aligned}
f(n)&=\sum\limits_{d \mid n}\mu(d)g\!\left(\frac{n}{d}\right) \\\\
\Leftrightarrow\quad
f(n)&=\sum\limits_{k \geq 1}\mu(k)g(kn).
\end{aligned}
$$
此处有
$$
\mu(n)=
\begin{cases}
1, & n=1 \\\\
0, & n \text{ 含有平方因子} \\\\
(-1)^k, & n \text{ 是 } k \text{ 个互不相同质因子的乘积}
\end{cases}
$$
并且有常用恒等式:
$$
\begin{aligned}
\sum\limits_{d \mid n}\mu(d)&=[n=1],\\\\
\phi(d)&=\sum\limits_{g \mid d}g\,\mu\!\left(\frac{d}{g}\right).
\end{aligned}
$$
在本题中,我们定义 $E(g)$ 为 $a_1,\ldots,a_{i-1}$ 中 $gcd(a_j,a_i)=g$ 的个数,以及 $C(d)$ 为 $a_1,\ldots,a_{i-1}$ 中 $d \mid a_j$ 的个数。
于是有,$\gcd(a_j,a_i)=g\ (g \mid a_i)$,等价于 $g \mid a_j$ 且 $\frac{a_j}{g}$ 与 $\frac{a_i}{g}$ 互质。用莫比乌斯函数展开即得到
$$
\begin{aligned}
[\gcd(x,m)=1]
&=\sum\limits_{t \mid \gcd(x,m)}\mu(t) \\\\
&=\sum\limits_{t \mid x,\ t \mid m}\mu(t).
\end{aligned}
$$
因此有
$$
\begin{aligned}
E(g)
&=\sum\limits_{j=1,\ g \mid a_j}^{i-1}\left[\gcd\!\left(\frac{a_j}{g},\frac{a_i}{g}\right)=1\right] \\\\
&=\sum\limits_{j=1,\ g \mid a_j}^{i-1}\sum\limits_{t \mid \frac{a_i}{g},\ t \mid \frac{a_j}{g}}\mu(t) \\\\
&=\sum\limits_{t \mid \frac{a_i}{g}}\mu(t)C(gt).
\end{aligned}
$$
于是
$$
\begin{aligned}
\sum\limits_{j=1}^{i-1}\gcd(a_j,a_i)
&=\sum\limits_{g \mid a_i}g\sum\limits_{t \mid \frac{a_i}{g}}\mu(t)C(gt) \\\\
&=\sum\limits_{d \mid a_i}C(d)\sum\limits_{g \mid d}g\,\mu\!\left(\frac{d}{g}\right) \\\\
&=\sum\limits_{d \mid a_i}\phi(d)C(d).
\end{aligned}
$$
做法同上,作者脑子烧了,先学欧拉反演吧。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
constexpr int MX = 1e5 + 1;
int phi[MX];
auto init = [] {
auto Phi = [](int n) -> int {
int res = n;
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) {
res = res / i * (i - 1);
while (n % i == 0) {
n /= i;
}
}
}
if (n > 1) res = res / n * (n - 1);
return res;
};
rep(i, 1, MX - 1) { phi[i] = Phi(i); }
return 0;
}();
vector<int> divisors[MX];
auto init2 = [] {
for (int i = 1; i < MX; i++) {
for (int j = i; j < MX; j += i) {
divisors[j].push_back(i);
}
}
return 0;
}();
void solve() {
int n;
cin >> n;
vi a(n);
rep(i, 0, n - 1) cin >> a[i];
ll ans = 0;
ranges::sort(a);
vi cnt(MX + 1);
rep(i, 0, n - 1) {
for (int& p : divisors[a[i]]) {
ans += 1LL * (n - 1 - i) * phi[p] * cnt[p];
}
for (int& p : divisors[a[i]]) {
cnt[p]++;
}
}
cout << ans << endl;
return;
}
|
New Year and Ancient Prohpecy
出处:CF611D
题目大意:给定一个 $n$ 位数,要求将其拆分为若干数字,满足数字的顺序要严格递增,都是正整数,且没有前导零,求所有可能的方案数,最后模 $10^9+7$ 。
数据范围:$1 \leq n \leq 5000$
思路:很容易能想到划分式DP,用 $dp[i][j]$ 表示当前结尾为 $i$ ,同时最后一段长度为 $j$ 的方案数,然后,如果前一段的长度小于这一段,那么就直接加上去(注意这里可以用前缀和优化)。
然后,如果前一段的长度跟这一段相等呢?这里采用LCP优化,即找到两段到结尾的子段最长的公共前缀,然后下一个字符就可以判断它们的大小关系了。
最后,记得判断前导零,这题疑似有点份。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
void solve() {
int n;
cin >> n;
string s;
cin >> s;
vvi dp(n + 1, vi(n + 1));
vvi pre(n + 1, vi(n + 1));
vvi lcp(n + 2, vi(n + 2));
frep(i, n, 1) {
frep(j, n, 1) {
if (s[i - 1] == s[j - 1]) lcp[i][j] = lcp[i + 1][j + 1] + 1;
}
}
rep(i, 1, n) dp[i][i] = 1;
rep(i, 1, n) {
rep(j, 1, i) {
if (j < i) {
if (i - 2 * j >= 0 && s[i - j] != '0') {
int l = lcp[i - 2 * j + 1][i - j + 1];
if (l < j && s[i - 2 * j + l] < s[i - j + l]) dp[i][j] = (dp[i][j] + dp[i - j][j]) % MOD;
}
if (s[i - j] != '0') dp[i][j] = (dp[i][j] + pre[i - j][j - 1]) % MOD;
}
pre[i][j] = (pre[i][j - 1] + dp[i][j]) % MOD;
}
rep(j, i + 1, n) pre[i][j] = pre[i][i];
}
ll ans = 0;
rep(i, 1, n) {
ans += dp[n][i] % MOD;
ans %= MOD;
}
cout << ans << endl;
return;
}
|
Directing Edges
出处:CF1385E
题目大意:
数据范围:
思路:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
void solve() {
int n, m, x, y, op;
cin >> n >> m;
vi ord(n);
vi deg(n);
vvi ma(n);
vector<pii> ma2;
vector<pii> ma3;
queue<int> q;
rep(i, 0, m - 1) {
cin >> op >> x >> y;
if (op == 0) {
ma2.emplace_back(x - 1, y - 1);
} else {
ma[x - 1].push_back(y - 1);
ma3.emplace_back(x - 1, y - 1);
deg[y - 1]++;
}
}
int cnt = 0;
vi tem;
rep(i, 0, n - 1) {
if (deg[i] == 0) {
q.push(i);
}
}
while (!q.empty()) {
auto node = q.front();
q.pop();
tem.push_back(node);
for (int& p : ma[node]) {
if (--deg[p] == 0) q.push(p);
}
}
if (sz(tem) != n) {
cout << "NO" << endl;
return;
}
cout << "YES" << endl;
rep(i, 0, n - 1) ord[tem[i]] = i;
for (auto& p : ma2) {
if (ord[p.first] > ord[p.second]) swap(p.first, p.second);
}
for (auto& p : ma2) cout << p.first + 1 << ' ' << p.second + 1 << endl;
for (auto& p : ma3) cout << p.first + 1 << ' ' << p.second + 1 << endl;
return;
}
|
Interacdive Problem
出处:CF1624F
题目大意:
数据范围:
思路:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
int ask(int l) {
cout << "+ " << l << '\n';
cout.flush();
int op;
cin >> op;
return op;
}
void report(ll a) {
cout << "! " << a << '\n';
cout.flush();
}
void solve() {
int n;
cin >> n;
int l = 1, r = n - 1, mid;
int sum = 0;
int ans = 1;
while (l <= r) {
mid = (l + r) / 2;
int tem = n - mid;
int tem2 = (tem - sum % n + n) % n;
sum += tem2;
int tem3 = ask(tem2);
if (tem3 == sum / n) {
r = mid - 1;
} else {
ans = mid;
l = mid + 1;
}
}
report(ans + sum);
return;
}
|
Salyg1n and Array (simple version)
出处:CF1867E1
题目大意:
数据范围:
思路:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
int ask(int l) {
cout << "? " << l << '\n';
cout.flush();
int op;
cin >> op;
return op;
}
void report(ll a) {
cout << "! " << a << '\n';
cout.flush();
}
void solve() {
int n, k;
cin >> n >> k;
if (n % k == 0) {
int tem = 0;
for (int i = 1; i <= n; i += k) {
int op = ask(i);
tem ^= op;
}
report(tem);
return;
}
int tem = 0;
for (int i = 1; i <= n / k * k; i += k) {
int op = ask(i);
tem ^= op;
}
int idx = n / k * k + 1;
rep(i, idx, n) {
int op = ask(i - k + 1);
tem ^= op;
}
report(tem);
return;
}
|
Geo Game
出处:CF1903E
题目大意:
数据范围:
思路:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
|
void ask(int l) {
cout << l << '\n';
cout.flush();
}
void report(ll a, ll b, ll c, ll d) {
cout << "! " << a << ' ' << b << ' ' << c << ' ' << d << '\n';
cout.flush();
}
void solve() {
int n, op;
cin >> n;
ll sx, sy;
cin >> sx >> sy;
vector<pll> ma(n);
rep(i, 0, n - 1) cin >> ma[i].first >> ma[i].second;
set<int> ma1;
set<int> ma2;
rep(i, 0, n - 1) {
if ((ma[i].first + ma[i].second) % 2 == 1)
ma1.insert(i + 1);
else
ma2.insert(i + 1);
}
ll tem = (sx + sy) % 2;
if (tem == 0) {
if (sz(ma1) > sz(ma2)) {
cout << "Second" << endl;
cout.flush();
rep(i, 1, n) {
if (i % 2) {
cin >> op;
if (ma1.count(op))
ma1.erase(op);
else
ma2.erase(op);
} else {
if (!ma2.empty()) {
ask(*ma2.begin());
ma2.erase(ma2.begin());
} else {
ask(*ma1.begin());
ma1.erase(ma1.begin());
}
}
}
} else {
cout << "First" << endl;
cout.flush();
rep(i, 1, n) {
if (i % 2) {
if (!ma1.empty()) {
ask(*ma1.begin());
ma1.erase(ma1.begin());
} else {
ask(*ma2.begin());
ma2.erase(ma2.begin());
}
} else {
cin >> op;
if (ma1.count(op))
ma1.erase(op);
else
ma2.erase(op);
}
}
}
} else {
if (sz(ma1) >= sz(ma2)) {
cout << "First" << endl;
cout.flush();
rep(i, 1, n) {
if (i % 2) {
if (!ma2.empty()) {
ask(*ma2.begin());
ma2.erase(ma2.begin());
} else {
ask(*ma1.begin());
ma1.erase(ma1.begin());
}
} else {
cin >> op;
if (ma1.count(op))
ma1.erase(op);
else
ma2.erase(op);
}
}
} else {
cout << "Second" << endl;
cout.flush();
rep(i, 1, n) {
if (i % 2) {
cin >> op;
if (ma1.count(op))
ma1.erase(op);
else
ma2.erase(op);
} else {
if (!ma1.empty()) {
ask(*ma1.begin());
ma1.erase(ma1.begin());
} else {
ask(*ma2.begin());
ma2.erase(ma2.begin());
}
}
}
}
}
return;
}
|
Infinite Maze
出处:CF196B
题目大意:
数据范围:
思路:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
int dx[4] = {0, 1, -1, 0};
int dy[4] = {1, 0, 0, -1};
void solve() {
int m, n;
cin >> m >> n;
vector<string> ma(m);
rep(i, 0, m - 1) cin >> ma[i];
int idx1 = -1, idx2 = -1;
rep(i, 0, m - 1) {
rep(j, 0, n - 1) {
if (ma[i][j] == 'S') {
idx1 = i, idx2 = j;
break;
}
}
}
vector<vector<pii>> vis(m, vector<pii>(n, make_pair(-1, -1)));
auto dfs = [&](this auto&& dfs, int x, int y, int tx, int ty) -> bool {
vis[x][y] = {tx, ty};
rep(i, 0, 3) {
int ax = (x + dx[i] + m) % m, ay = (y + dy[i] + n) % n;
int bx = tx + dx[i], by = ty + dy[i];
if (ma[ax][ay] == '#') continue;
if (vis[ax][ay] == make_pair(-1, -1)) {
if (dfs(ax, ay, bx, by)) return true;
} else {
if (vis[ax][ay] != make_pair(bx, by)) return true;
}
}
return false;
};
bool flag = dfs(idx1, idx2, idx1, idx2);
cout << (flag ? "Yes" : "No") << endl;
return;
}
|
Appleman and Tree
出处:CF461B
题目大意:
数据范围:
思路:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
const ll MOD = 1e9 + 7;
void solve() {
int n, x;
cin >> n;
vvi ma(n);
rep(i, 1, n - 1) {
cin >> x;
ma[x].push_back(i);
ma[i].push_back(x);
}
vi c(n);
rep(i, 0, n - 1) cin >> c[i];
auto dfs = [&](this auto&& dfs, int x, int pa) -> pll {
ll tem = 0, tem2 = 0;
if (c[x] == 0)
tem = 1;
else
tem2 = 1;
for (int& p : ma[x]) {
if (p == pa) continue;
auto [l1, l2] = dfs(p, x);
ll temp = tem, temp2 = tem2;
tem = temp * (l1 + l2) % MOD;
tem2 = (temp * l2 % MOD + temp2 * (l1 + l2) % MOD) % MOD;
}
return {tem, tem2};
};
auto [tem, tem2] = dfs(0, -1);
cout << tem2 << endl;
return;
}
|
Array Queries
出处:CF797E
题目大意:
数据范围:
思路:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
void solve() {
int n, q, p, k;
cin >> n;
int B = sqrt(n);
vl a(n);
rep(i, 0, n - 1) cin >> a[i];
cin >> q;
vvi suf(B + 1, vi(n));
rep(i, 1, B) {
frep(j, n - 1, 0) {
suf[i][j] = 1;
if (j + a[j] + i <= n - 1) suf[i][j] += suf[i][j + a[j] + i];
}
}
rep(i, 0, q - 1) {
cin >> p >> k;
p--;
if (k > B) {
int tem = 0;
while (p <= n - 1) {
tem++;
p += a[p] + k;
}
cout << tem << endl;
} else {
cout << suf[k][p] << endl;
}
}
return;
}
|
Mahmoud and Ehab and the binary string
出处:CF862D
题目大意:
数据范围:
思路:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
int ask(string l) {
cout << "? " << l << '\n';
cout.flush();
int op;
cin >> op;
return op;
}
void report(ll a, ll b) {
cout << "! " << a << ' ' << b << '\n';
cout.flush();
}
void solve() {
int n;
cin >> n;
int idx0 = -1, idx1 = -1;
string s, t;
rep(i, 0, n - 1) s.push_back('0');
t = s;
int tem = ask(s);
s[0] = '1';
int tem2 = ask(s);
if (tem == tem2 + 1) {
idx1 = 0;
} else {
idx0 = 0;
}
if (idx1 != -1) {
int l = 0, r = n - 1, mid, ans = 0;
while (l <= r) {
mid = (l + r) / 2;
string tem3 = t;
rep(i, l, mid) tem3[i] = '1';
int tem4 = ask(tem3);
if ((mid - l + 1) - (tem + mid - l + 1 - tem4) / 2 > 0) {
ans = mid;
r = mid - 1;
} else
l = mid + 1;
}
idx0 = ans;
} else {
int l = 0, r = n - 1, mid, ans = 0;
while (l <= r) {
mid = (l + r) / 2;
string tem3 = t;
rep(i, l, mid) tem3[i] = '1';
int tem4 = ask(tem3);
if ((tem + mid - l + 1 - tem4) / 2 > 0) {
ans = mid;
r = mid - 1;
} else
l = mid + 1;
}
idx1 = ans;
}
report(idx0 + 1, idx1 + 1);
return;
}
|
Yet Another Yet Another Task
出处:CF1359D
题目大意:
数据范围:
思路:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
struct Info {
ll sum, pre, suf, ans;
Info(ll x = 0) : sum(x), pre(x), suf(x), ans(x) {}
};
Info operator+(const Info& a, const Info& b) {
Info c;
c.sum = a.sum + b.sum;
c.pre = max(a.pre, a.sum + b.pre);
c.suf = max(b.suf, b.sum + a.suf);
c.ans = max({a.ans, b.ans, a.suf + b.pre});
return c;
}
template <typename T>
class SegmentTree {
int n;
vector<T> tree;
T merge_val(T a, T b) const { return a + b; } // 合并子树
void maintain(int node) { // 维护整棵树
tree[node] = merge_val(tree[node * 2], tree[node * 2 + 1]);
}
void build(const vector<T>& a, int node, int l, int r) {
if (l == r) {
tree[node] = a[l];
return;
}
int m = (l + r) / 2;
build(a, node * 2, l, m);
build(a, node * 2 + 1, m + 1, r);
maintain(node);
} // 建树
void update(int node, int l, int r, int i, T val) {
if (l == r) {
tree[node] = val;
return;
}
int m = (l + r) / 2;
if (i <= m)
update(node * 2, l, m, i, val);
else
update(node * 2 + 1, m + 1, r, i, val);
maintain(node);
} // 更新i处的值为val
T query(int node, int l, int r, int ql, int qr) const {
if (ql <= l && r <= qr) return tree[node];
int m = (l + r) / 2;
if (qr <= m) return query(node * 2, l, m, ql, qr);
if (ql > m) return query(node * 2 + 1, m + 1, r, ql, qr);
T l_res = query(node * 2, l, m, ql, qr);
T r_res = query(node * 2 + 1, m + 1, r, ql, qr);
return merge_val(l_res, r_res);
} // 查询[ql,qr]的值
int find_first(int node, int l, int r, int ql, int qr, T val) const {
if (r < ql || l > qr) return -1;
if (tree[node] < val) return -1;
if (l == r) return l;
int m = (l + r) >> 1;
int res = find_first(node << 1, l, m, ql, qr, val);
if (res != -1) return res;
return find_first(node << 1 | 1, m + 1, r, ql, qr, val);
} // 若遇到固定左端点的情况,需要使用全局变量(或者传入引用)记录前缀分段最大值,加一个被待求区间完全覆盖的剪枝
int find_last(int node, int l, int r, int ql, int qr, T val) const {
if (r < ql || l > qr) return -1;
if (tree[node] < val) return -1;
if (l == r) return l;
int m = (l + r) >> 1;
int res = find_last(node << 1 | 1, m + 1, r, ql, qr, val);
if (res != -1) return res;
return find_last(node << 1, l, m, ql, qr, val);
}
public:
SegmentTree(int n, T init_val) : SegmentTree(vector<T>(n, init_val)) {}
SegmentTree(const vector<T>& a) : n(a.size()), tree(2 << bit_width(a.size() - 1)) { build(a, 1, 0, n - 1); } // 传入一个数组维护
void update(int i, T val) { update(1, 0, n - 1, i, val); } // 更新i的值为val
T query(int ql, int qr) const { return query(1, 0, n - 1, ql, qr); } // 查询[ql,qr]的值
T get(int i) const { return query(1, 0, n - 1, i, i); } // 取出i处的值
int find_first(int ql, int qr, T val) const { return find_first(1, 0, n - 1, ql, qr, val); } // 查询[ql,qr]中第一个满足条件的下标
int find_last(int ql, int qr, T val) const { return find_last(1, 0, n - 1, ql, qr, val); } // 查询[ql,qr]中最后一个满足条件的下标
};
void solve() {
int n;
cin >> n;
vl a(n);
rep(i, 0, n - 1) cin >> a[i];
vector<Info> b(n);
rep(i, 0, n - 1) b[i] = Info(a[i]);
SegmentTree<Info> seg(b);
vector<int> r(n, n);
vector<int> l(n, -1);
stack<int> s;
for (int i = n - 1; i >= 0; i--) {
while (!s.empty() && a[s.top()] <= a[i]) s.pop();
if (!s.empty()) r[i] = s.top();
s.push(i);
} // 求右边第一个小于的下标
while (!s.empty()) s.pop();
for (int i = 0; i <= n - 1; i++) {
while (!s.empty() && a[s.top()] <= a[i]) s.pop();
if (!s.empty()) l[i] = s.top();
s.push(i);
} // 求左边第一个小于的下标
ll ans = 0;
rep(i, 0, n - 1) {
Info left = seg.query(l[i] + 1, i);
Info right = seg.query(i, r[i] - 1);
ans = max(ans, left.suf + right.pre - 2 * a[i]);
}
cout << ans << endl;
return;
}
|
Odd-Even Subsequence
出处:CF1370D
题目大意:
数据范围:
思路:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
void solve() {
int n, k;
cin >> n >> k;
vi a(n);
rep(i, 0, n - 1) cin >> a[i];
auto check = [&](int mid) -> bool {
int cnt = 0;
rep(i, 0, n - 1) {
if (cnt % 2 == 0) {
if (a[i] > mid)
continue;
else
cnt++;
} else
cnt++;
}
if (cnt >= k) return true;
cnt = 0;
rep(i, 0, n - 1) {
if (cnt % 2 == 1) {
if (a[i] > mid)
continue;
else
cnt++;
} else
cnt++;
}
return cnt >= k;
};
int l = 1, r = 1e9, mid, ans = 1;
while (l <= r) {
mid = (l + r) / 2;
if (check(mid)) {
ans = mid;
r = mid - 1;
} else
l = mid + 1;
}
cout << ans << endl;
return;
}
|
Count Paths
出处:CF1923E
题目大意:
数据范围:
思路:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
void solve() {
int n, x, y;
cin >> n;
vi co(n);
rep(i, 0, n - 1) cin >> co[i];
vvi ma(n);
rep(i, 1, n - 1) {
cin >> x >> y;
ma[x - 1].push_back(y - 1);
ma[y - 1].push_back(x - 1);
}
ll ans = 0;
auto dfs = [&](this auto&& dfs, int x, int pa) -> map<int, int> {
map<int, int> ma2;
int c = co[x];
for (int& p : ma[x]) {
if (p == pa) continue;
auto ma3 = dfs(p, x);
if (sz(ma2) < sz(ma3)) {
swap(ma2, ma3);
}
if (ma2.count(c)) {
ans += 1LL * ma2[c];
ma2.erase(c);
}
for (auto& [y, z] : ma3) {
if (y == c)
ans += 1LL * z;
else {
if (ma2.count(y)) ans += 1LL * ma2[y] * z;
ma2[y] += z;
}
}
}
ma2[c] = 1;
return ma2;
};
dfs(0, -1);
cout << ans << endl;
return;
}
|