Blog of RuSun

\begin {array}{c} \mathfrak {One Problem Is Difficult} \\\\ \mathfrak {Because You Don't Know} \\\\ \mathfrak {Why It Is Diffucult} \end {array}

ARC137

ARC137

A - Coprime Pair

给定区间,求区间中互质的两个数的差的最大值。

可以发现,很快可以发现答案,直接暴力做就是了。

查看代码
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
#include <cstdio>
using namespace std;
typedef long long LL;
template <class Type>
void read(Type &x)
{
char c;
bool flag = false;
while ((c = getchar()) < '0' || c > '9')
c == '-' && (flag = true);
x = c - '0';
while ((c = getchar()) >= '0' && c <= '9')
x = (x << 3) + (x << 1) + c - '0';
flag && (x = ~x + 1);
}
template <class Type>
void write(Type x)
{
x < 0 && (putchar('-'), x = ~x + 1);
x > 9 && (write(x / 10), 0);
putchar(x % 10 + '0');
}
LL L, R;
LL gcd(LL a, LL b)
{
return b ? gcd(b, a % b) : a;
}
int main()
{
read(L), read(R);
for (LL k = R - L; k; k--)
{
bool flag = false;
for (LL i = L, j = L + k; j <= R && !flag; i++, j++)
gcd(i, j) == 1 && (flag = true);
if (flag)
{
printf("%lld\n", k);
break;
}
}
return 0;
}

B - Count 1’s

给定一个 01 串,可以进行一次操作,使得一段区间的 $0 \to 1, 1 \to 0$ ,求 $1$ 的个数可能有多少个取值。

首先可以发现答案一定是连续的,即存在最小值 $mn$ 和最大值 $mx$ ,那么中间的任意 $x \in [mn, mx]$ ,都存在方案。

考虑如何求最大值和最小值,将 $1$ 的个数做前缀和。

如果翻转 $[l + 1, r]$ ,那么 $1$ 的个数为:
$$
\begin {aligned}
& s _ l + (r - l) - (s _ r - s _ l) + (s _ n - s _ r) \\
= & (s _ n - l + 2s _ l) + (r - 2 s _ r)
\end {aligned}
$$
当 $l$ 确定时,选择 $r - 2 s _ r$​ 的最值即可。

查看代码
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
#include <cstdio>
#include <algorithm>
using namespace std;
template <class Type>
void read(Type &x)
{
char c;
bool flag = false;
while ((c = getchar()) < '0' || c > '9')
c == '-' && (flag = true);
x = c - '0';
while ((c = getchar()) >= '0' && c <= '9')
x = (x << 3) + (x << 1) + c - '0';
flag && (x = ~x + 1);
}
template <class Type>
void write(Type x)
{
x < 0 && (putchar('-'), x = ~x + 1);
x > 9 && (write(x / 10), 0);
putchar(x % 10 + '0');
}
const int N = 3e5 + 10;
int n, w[N], s[N], f[N], g[N];
int main()
{
read(n);
for (int i = 1; i <= n; i++)
read(w[i]), s[i] = s[i - 1] + w[i];
for (int i = 1; i <= n; i++)
f[i] = g[i] = i - s[i] * 2;
for (int i = n - 1; i; i--)
f[i] = max(f[i], f[i + 1]), g[i] = min(g[i], g[i + 1]);
int mx = s[n], mn = s[n];
for (int i = 1; i <= n; i++)
{
mx = max(mx, s[n] - i + 1 + 2 * s[i - 1] + f[i]);
mn = min(mn, s[n] - i + 1 + 2 * s[i - 1] + g[i]);
}
printf("%d\n", mx - mn + 1);
return 0;
}

C - Distinct Numbers

结论:任何时候如果最大的两个数间隔大于 $1$ ,那么先手赢。

感性理解:前面有很多个空位,作为先手,可以选择补充一个前面的空位;也可以选择使得 $A _ n = A _ {n - 1} + 1$ ,令后手不得不去补前面一个空位。当没有空位的时候就输了,所以一直优势在我。

如果不存在这种情况,那么一定双方保证任意时候最大的两个数间隔不大于 $1$ ,那么最大值一定一次只能减少 $1$ ,当最大值为 $n - 1$ 的时候游戏结束,可以进行 $A _ n - (n - 1)$​ 次操作,判断奇偶性即可。

查看代码
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
#include <cstdio>
#include <algorithm>
using namespace std;
template <class Type>
void read(Type &x)
{
char c;
bool flag = false;
while ((c = getchar()) < '0' || c > '9')
c == '-' && (flag = true);
x = c - '0';
while ((c = getchar()) >= '0' && c <= '9')
x = (x << 3) + (x << 1) + c - '0';
flag && (x = ~x + 1);
}
template <class Type>
void write(Type x)
{
x < 0 && (putchar('-'), x = ~x + 1);
x > 9 && (write(x / 10), 0);
putchar(x % 10 + '0');
}
const int N = 3e5 + 10;
int n, w[N];
int main()
{
read(n);
for (int i = 1; i <= n; i++)
read(w[i]);
puts(w[n - 1] + 1 < w[n] || w[n] - (n - 1) & 1 ? "Alice" : "Bob");
return 0;
}

D - Prefix XORs

写出来几项找规律,发现一个数对答案有贡献当且仅当 $\binom {n - i + k - 1}{k - 1}$ 为奇数。

考虑卢卡斯定理,相当于对 $n - i + k - 1$ 和 $k - 1$ 二进制分解,考虑每一位乘起来,发现
$$
\binom {0}{0} = 1
$$

$$
\binom {0}{1} = 0
$$

$$
\binom {1}{0} = 1
$$

$$
\binom {1}{1} = 1
$$

也就是一旦出现某一位 $n - i + k - 1$ 为 $0$ 但是 $k - 1$ 为 $1$ 就没有贡献了,也就是 $k - 1$ 是 $n - i + k - 1$ 的子集,$n - i$ 是与 $k - 1$ 不相交的 $n - i + k - 1$ 的子集。如果将 $n - i$ 每一位取反,那么 $k - 1$ 是 $n - i$ 的子集 。记 $S$ 为一个所有位都是 $1$ 的数,那么 $k - 1 \subseteq S - (n - i)$ 。考虑 FMT ,用类似高维后缀和的方式可以快速地求出所有的答案。

查看代码
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
#include <cstdio>
#include <algorithm>
using namespace std;
template <class Type>
void read(Type &x)
{
char c;
bool flag = false;
while ((c = getchar()) < '0' || c > '9')
c == '-' && (flag = true);
x = c - '0';
while ((c = getchar()) >= '0' && c <= '9')
x = (x << 3) + (x << 1) + c - '0';
flag && (x = ~x + 1);
}
template <class Type>
void write(Type x)
{
x < 0 && (putchar('-'), x = ~x + 1);
x > 9 && (write(x / 10), 0);
putchar(x % 10 + '0');
}
const int N = 5e6 + 10;
int n, m, w[N], v[N];
int main()
{
read(n), read(m);
for (int i = 0; i < n; i++)
read(w[i]);
int bit = 1;
while (1 << bit < max(n, m))
bit++;
int tot = 1 << bit;
for (int i = 0; i < n; i++)
v[tot - n + i] = w[i];
for (int i = 0; i < bit; i++)
for (int j = 0; j < tot; j++)
(j >> i & 1) && (v[j ^ 1 << i] ^= v[j]);
for (int i = 0; i < m; i++)
write(v[i]), putchar(' ');
return 0;
}

E - Bakery

看到题一下想到了 NOI2008 志愿者招募,基本一模一样。

多了一个放弃一个人 $D$ 的收益,相当于多一种人仅贡献当天,花费 $D$ 的代价。

——然而板子被卡了???

赛后将 spfa 换成 dijkstra 就过了。

分析复杂度:本题和志愿者招募不同的是这道题一个人只能用一次,那么流量上界为 $m$ 。那么 dijkstra 保证了复杂度为 $O(n ^ 2 \log n)$ ,用 spfa 大概为 $O(n ^ 3)$ 。其中 $n = 2 \times 10 ^ 3$​ 。dijkstra 刚刚过,spfa 被卡飞。

查看代码
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
123
124
125
126
127
128
129
130
131
132
133
134
135
#include <cstdio>
#include <queue>
using namespace std;
typedef long long LL;
typedef pair<LL, int> PLI;
template <class Type>
void read(Type &x)
{
char c;
bool flag = false;
while ((c = getchar()) < '0' || c > '9')
c == '-' && (flag = true);
x = c - '0';
while ((c = getchar()) >= '0' && c <= '9')
x = (x << 3) + (x << 1) + c - '0';
flag && (x = ~x + 1);
}
template <class Type>
void write(Type x)
{
x < 0 && (putchar('-'), x = ~x + 1);
x > 9 && (write(x / 10), 0);
putchar(x % 10 + '0');
}
const LL inf = 4e15;
const int N = 1e4 + 10, M = 2e5 + 10;
bool vis[N];
LL h[N], d[N], wt[M], cst[M];
int n, m, st, ed, D, pre[N];
int idx = -1, hd[N], nxt[M], edg[M];
void spfa()
{
for (int i = st; i <= ed; i++)
h[i] = inf;
queue<int> q;
q.push(st);
h[st] = 0;
vis[st] = true;
while (!q.empty())
{
int t = q.front();
q.pop();
vis[t] = false;
for (int i = hd[t]; ~i; i = nxt[i])
if (wt[i] && h[t] + cst[i] < h[edg[i]])
{
h[edg[i]] = h[t] + cst[i];
if (!vis[edg[i]])
{
vis[edg[i]] = true;
q.push(edg[i]);
}
}
}
}
bool dijkstra()
{
for (int i = st; i <= ed; i++)
{
vis[i] = false;
d[i] = inf;
}
priority_queue<PLI, vector<PLI>, greater<PLI>> q;
d[st] = 0;
q.push(make_pair(0, st));
while (!q.empty())
{
int t = q.top().second;
q.pop();
if (vis[t])
continue;
vis[t] = true;
for (int i = hd[t]; ~i; i = nxt[i])
if (wt[i] && d[t] + cst[i] + h[t] - h[edg[i]] < d[edg[i]])
{
d[edg[i]] = d[t] + cst[i] + h[t] - h[edg[i]];
pre[edg[i]] = i;
q.push(make_pair(d[edg[i]], edg[i]));
}
}
return d[ed] != inf;
}
void add(int a, int b, LL c, LL d)
{
nxt[++idx] = hd[a];
hd[a] = idx;
edg[idx] = b;
wt[idx] = c;
cst[idx] = d;
}
LL PrimalDual()
{
spfa();
LL res = 0;
while (dijkstra())
{
for (int i = st; i <= ed; i++)
h[i] += d[i];
LL t = inf;
for (int i = ed; i != st; i = edg[pre[i] ^ 1])
t = min(t, wt[pre[i]]);
for (int i = ed; i != st; i = edg[pre[i] ^ 1])
wt[pre[i]] -= t, wt[pre[i] ^ 1] += t;
res += t * h[ed];
}
return res;
}
int main()
{
read(n), read(m), read(D);
st = 0;
ed = n + 1;
for (int i = st; i <= ed; i++)
hd[i] = -1;
int tot = 0;
for (int i = 0, a; i < n; i++)
{
read(a);
tot += a;
add(i, i + 1, inf - a, 0);
add(i + 1, i, 0, 0);
add(i, i + 1, a, D);
add(i + 1, i, 0, -D);
}
add(n, ed, inf, 0);
add(ed, n, 0, 0);
for (int i = 1, a, b, c; i <= m; i++)
{
read(a), read(b), read(c);
add(a - 1, b, 1, c);
add(b, a - 1, 0, -c);
}
write((LL)tot * D - PrimalDual());
return 0;
}