目录
奇怪装置
题目描述
给定 $A, B$ 以及 $n$ 个时间区间,时钟在时刻 $t$ 会显示 $(t + \lfloor \frac{t}{B} \rfloor \bmod A, t \bmod B)$。问共会出现多少个不同的时刻。
数据范围:$n \le 10^6, 1 \le AB \le 10^{18}, 0 \le l_i \le r_i \le 10^{18}$。
思路分析
考虑两个时刻什么时候显示的二元组相等。设 $t_2 = t_1 + m B$,那么有:
设 $\lfloor \frac{t_1}{B} \rfloor = k$,则:
所以 $A \vert m(B + 1)$,那么 $m$ 的最小值就是 $\frac{A}{\gcd(A, B + 1)}$,也就是说循环节的长度为 $\frac{AB}{\gcd(A, B + 1)}$。这样我们对所有时间区间处理一下然后用扫描线求它们的交即可。时间复杂度 $O(n \log n)$。
代码实现
1 |
|
2 | using namespace std; |
3 | |
4 | typedef long long ll; |
5 | const int maxn = 4e6; |
6 | int n, m, tot, sum[maxn + 3]; |
7 | ll A, B, C, l, r, temp[maxn + 3]; |
8 | pair<ll, ll> sect[maxn + 3]; |
9 | |
10 | ll gcd(ll a, ll b) { |
11 | return b ? gcd(b, a % b) : a; |
12 | } |
13 | |
14 | int main() { |
15 | scanf("%d %lld %lld", &n, &A, &B); |
16 | C = A / gcd(A, B + 1) * B; |
17 | for (int i = 1; i <= n; i++) { |
18 | scanf("%lld %lld", &l, &r); |
19 | if (r - l + 1 >= C) { |
20 | printf("%lld\n", C); |
21 | return 0; |
22 | } |
23 | l %= C, r %= C; |
24 | if (l <= r) { |
25 | sect[++tot] = make_pair(l, r); |
26 | } else { |
27 | sect[++tot] = make_pair(0, r); |
28 | sect[++tot] = make_pair(l, C - 1); |
29 | } |
30 | } |
31 | for (int i = 1; i <= tot; i++) { |
32 | temp[++m] = sect[i].first; |
33 | temp[++m] = sect[i].second + 1; |
34 | } |
35 | sort(temp + 1, temp + m + 1); |
36 | for (int i = 1; i <= tot; i++) { |
37 | int x = lower_bound(temp + 1, temp + m + 1, sect[i].first) - temp; |
38 | int y = lower_bound(temp + 1, temp + m + 1, sect[i].second + 1) - temp; |
39 | sum[x]++, sum[y]--; |
40 | } |
41 | ll ans = 0; |
42 | for (int i = 1; i <= m; i++) { |
43 | sum[i] += sum[i - 1]; |
44 | if (sum[i]) { |
45 | ans += temp[i + 1] - temp[i]; |
46 | } |
47 | } |
48 | printf("%lld\n", ans); |
49 | return 0; |
50 | } |
桥梁
题目描述
给定一个带权无向图,支持修改边权,以及询问从一个点出发只经过边权不小于 $w$ 的边能到达多少个点。
数据范围:$n \le 5 \times 10^4, m, q \le 10^5$。
思路分析
如果没有修改可以离线做,有了修改后我们就考虑对操作分块,之前的修改离线加入,块内的修改暴力加入。具体地,我们将所有操作分成大小为 $b$ 的块。对于某一个块,我们离线地回答块内部的所有询问。
这时,我们可以把边分成两类:在本块内部有修改的和没有修改的。对于没有修改的边,我们将它们排序,然后离线的做;对于有修改的,它们的数量肯定不超过 $b$,我们就对于每个询问扫一遍这些边,看一下在这个询问的时间点上,它的权值是多少。这样如果 $b = \sqrt q$,那么时间复杂度是 $O(q \sqrt q \log m)$ 的。
需要注意算法的常数优化。有两个小技巧:
- 并查集在执行 $\text{fa}[u] \leftarrow v, \text{size}[v] \leftarrow \text{size}[u] + \text{size}[v]$ 后如果想要撤销,我们只需要记录 $u$,因为剩下的都可以倒推出来。
- 维护所有边按权值排序的数组时,加入每一块时不需要整体排序,只要做一次块内排序和一次归并即可。归并可以使用
std::merge()
函数,具体见代码。
另外,经试验,分块时块的大小 $b$ 为 $750$ 时常数较为优秀。
代码实现
1 |
|
2 | using namespace std; |
3 | |
4 | namespace __main__ { |
5 | const int maxn = 5e4, maxm = 1e5, b = 750, maxb = b * 2; |
6 | int n, m, q, w[maxm + 3], tot, vis[maxm + 3], last[maxm + 3]; |
7 | int cnt, lft[maxb + 3], rht[maxb + 3], num[maxb + 3], wei[maxb + 3]; |
8 | int all, temp[maxb + 3], cur[maxm + 3], ans[maxm + 3]; |
9 | int fa[maxn + 3], sz[maxn + 3], top, st[maxb + 3]; |
10 | pair<int, int> e[maxm + 3], pr[maxm + 3], t_1[maxm + 3], t_2[maxb + 3]; |
11 | |
12 | struct event { |
13 | int id, op, x, w; |
14 | friend bool operator < (const event &a, const event &b) { |
15 | return a.id < b.id; |
16 | } |
17 | } ev[maxm + 3]; |
18 | |
19 | bool comp_w(const event &a, const event &b) { |
20 | return a.w < b.w; |
21 | } |
22 | |
23 | void get_mods(int l, int r) { |
24 | ++tot, cnt = 0, all = 0; |
25 | for (int i = l; i <= r; i++) { |
26 | if (ev[i].op == 1) { |
27 | if (vis[ev[i].x] != tot) { |
28 | vis[ev[i].x] = tot; |
29 | last[ev[i].x] = l; |
30 | cur[ev[i].x] = w[ev[i].x]; |
31 | temp[++all] = ev[i].x; |
32 | } |
33 | lft[++cnt] = last[ev[i].x], rht[cnt] = i - 1; |
34 | num[cnt] = ev[i].x, wei[cnt] = cur[ev[i].x]; |
35 | last[ev[i].x] = i + 1, cur[ev[i].x] = ev[i].w; |
36 | } |
37 | } |
38 | for (int i = 1; i <= all; i++) { |
39 | lft[++cnt] = last[temp[i]], rht[cnt] = r; |
40 | num[cnt] = temp[i], wei[cnt] = cur[temp[i]]; |
41 | } |
42 | } |
43 | |
44 | int find(int x) { |
45 | return x == fa[x] ? x : find(fa[x]); |
46 | } |
47 | |
48 | void add(int u, int v, bool flag) { |
49 | u = find(u), v = find(v); |
50 | if (u == v) return; |
51 | if (sz[u] < sz[v]) swap(u, v); |
52 | if (flag) st[++top] = v; |
53 | fa[v] = u, sz[u] += sz[v]; |
54 | } |
55 | |
56 | void add(int x, bool flag) { |
57 | add(e[x].first, e[x].second, flag); |
58 | } |
59 | |
60 | void cancel() { |
61 | int u = st[top--]; |
62 | sz[fa[u]] -= sz[u], fa[u] = u; |
63 | } |
64 | |
65 | void main() { |
66 | scanf("%d %d", &n, &m); |
67 | for (int i = 1, x, y, z; i <= m; i++) { |
68 | scanf("%d %d %d", &x, &y, &z); |
69 | x > y ? swap(x, y) : void(); |
70 | e[i] = make_pair(x, y), w[i] = -z; |
71 | } |
72 | for (int i = 1; i <= m; i++) { |
73 | pr[i] = make_pair(w[i], i); |
74 | } |
75 | sort(pr + 1, pr + m + 1); |
76 | scanf("%d", &q); |
77 | for (int i = 1; i <= q; i++) { |
78 | scanf("%d %d %d", &ev[i].op, &ev[i].x, &ev[i].w); |
79 | ev[i].id = i, ev[i].w = -ev[i].w; |
80 | } |
81 | for (int l = 1, r = min(q, b); l <= q; l += b, r = min(q, r + b)) { |
82 | get_mods(l, r); |
83 | sort(ev + l, ev + r + 1, comp_w); |
84 | for (int i = 1; i <= n; i++) { |
85 | fa[i] = i, sz[i] = 1; |
86 | } |
87 | top = 0; |
88 | int p = 1; |
89 | for (int i = l; i <= r; i++) { |
90 | if (ev[i].op == 2) { |
91 | while (p <= m && pr[p].first <= ev[i].w) { |
92 | if (vis[pr[p].second] != tot) { |
93 | add(pr[p].second, false); |
94 | } |
95 | p++; |
96 | } |
97 | } |
98 | for (int j = 1; j <= cnt; j++) { |
99 | if (ev[i].id >= lft[j] && ev[i].id <= rht[j] && ev[i].w >= wei[j]) { |
100 | add(num[j], true); |
101 | } |
102 | } |
103 | ans[ev[i].id] = sz[find(ev[i].x)]; |
104 | while (top) { |
105 | cancel(); |
106 | } |
107 | } |
108 | sort(ev + l, ev + r + 1); |
109 | for (int i = l; i <= r; i++) { |
110 | if (ev[i].op == 2) { |
111 | printf("%d\n", ans[i]); |
112 | } |
113 | } |
114 | for (int i = l; i <= r; i++) { |
115 | if (ev[i].op == 1) { |
116 | w[ev[i].x] = ev[i].w; |
117 | } |
118 | } |
119 | cnt = 0, all = 0; |
120 | for (int i = 1, id; i <= m; i++) { |
121 | id = pr[i].second; |
122 | if (vis[id] != tot) t_1[++cnt] = pr[i]; |
123 | else t_2[++all] = make_pair(w[id], id); |
124 | } |
125 | sort(t_2 + 1, t_2 + all + 1); |
126 | merge(t_1 + 1, t_1 + cnt + 1, t_2 + 1, t_2 + all + 1, pr + 1); |
127 | } |
128 | } |
129 | } |
130 | |
131 | int main() { |
132 | __main__::main(); |
133 | return 0; |
134 | } |
路灯
在路上了。
题目描述
有 $n + 1$ 个位置,$n$ 条道路,第 $i$ 条连接第 $i$ 个位置和第 $i + 1$ 个位置。一开始时有些边是不可用的,有 $q$ 次操作,每次更改一条边的可用性,或者查询某两个点在历史中联通过多少个时刻。
数据范围:$n, q \le 3 \times 10^5$。
思路分析
将询问看作二维平面上的点,考虑一次修改的贡献。发现两点之间连通的时刻一定是形如:$t_1$ 时刻联通,$t_2$ 时刻断开,$t_3$ 时刻联通…… 的样子,所以我们考虑差分。设总时刻数为 $T$,当前时刻为 $t$。对于一次修改,如果是将两个区间联通,我们给这两个区间对应的矩形加上 $T - t$;否则它会把某个区间断开,我们给断开后分成的两个区间对应的矩形减去 $T - t$。另外,如果询问时如果两点联通,答案要额外地减去 $T - t$。
这样,我们就把问题转化成了一个带修改的二维数点的问题,使用 CDQ 分治套树状数组 / 树套树 / K-D Tree 即可。时间复杂度 $O(n\log^2 n)$ 或 $O(n \sqrt n)$。
代码实现
1 |
|
2 | using namespace std; |
3 | |
4 | typedef long long ll; |
5 | const int maxn = 3e5, maxm = maxn * 8, inf = 1e9; |
6 | int n, q, a[maxn + 3], m, tot; |
7 | char s[10]; |
8 | set<pair<int, int> > S; |
9 | ll bit[maxn + 3], ans[maxn + 3]; |
10 | |
11 | struct event { |
12 | int id, type, x, y, z; |
13 | friend bool operator < (const event &a, const event &b) { |
14 | return a.x < b.x; |
15 | } |
16 | } ev[maxm + 3]; |
17 | |
18 | void add_ev(int type, int x, int y, int z) { |
19 | ++tot, ev[tot].id = tot, ev[tot].type = type; |
20 | ev[tot].x = x, ev[tot].y = y, ev[tot].z = z; |
21 | } |
22 | |
23 | void add(int u, int d, int l, int r, int t) { |
24 | d++, r++; |
25 | add_ev(1, u, l, t), add_ev(1, d, r, t); |
26 | add_ev(1, u, r, -t), add_ev(1, d, l, -t); |
27 | } |
28 | |
29 | void mod(int x, int y) { |
30 | for (int i = x; i <= n; i += i & -i) { |
31 | bit[i] += y; |
32 | } |
33 | } |
34 | |
35 | ll sum(int x) { |
36 | ll y = 0; |
37 | for (int i = x; i; i ^= i & -i) { |
38 | y += bit[i]; |
39 | } |
40 | return y; |
41 | } |
42 | |
43 | void cdq(int l, int r) { |
44 | if (l == r) { |
45 | return; |
46 | } |
47 | int mid = (l + r) >> 1; |
48 | cdq(l, mid), cdq(mid + 1, r); |
49 | inplace_merge(ev + l, ev + mid + 1, ev + r + 1); |
50 | for (int i = l; i <= r; i++) { |
51 | if (ev[i].id <= mid && ev[i].type == 1) { |
52 | mod(ev[i].y, ev[i].z); |
53 | } else if (ev[i].id > mid && ev[i].type == 2) { |
54 | ans[ev[i].z] += sum(ev[i].y); |
55 | } |
56 | } |
57 | for (int i = l; i <= r; i++) { |
58 | if (ev[i].id <= mid && ev[i].type == 1) { |
59 | mod(ev[i].y, -ev[i].z); |
60 | } |
61 | } |
62 | } |
63 | |
64 | int main() { |
65 | scanf("%d %d", &n, &q); |
66 | for (int i = 1; i <= n; i++) { |
67 | scanf("%1d", &a[i]); |
68 | } |
69 | S.insert(make_pair(-inf, -inf)), S.insert(make_pair(inf, inf)); |
70 | for (int i = 1, j = 1; i <= n; i = j + 1, j = i) { |
71 | if (!a[i]) continue; |
72 | add(i, i, i, i, q); |
73 | while (j < n && a[j + 1] == 1) { |
74 | j++, add(i, j, j, j, q); |
75 | } |
76 | S.insert(make_pair(i, j)); |
77 | } |
78 | set<pair<int, int> >::iterator it, ti; |
79 | for (int i = 1, x, y, l, r; i <= q; i++) { |
80 | scanf("%s", s + 1); |
81 | if (s[1] == 't') { |
82 | scanf("%d", &x); |
83 | if (a[x]) { |
84 | it = S.lower_bound(make_pair(x + 1, 0)), it--; |
85 | l = it -> first, r = it -> second, S.erase(it); |
86 | if (l < x) S.insert(make_pair(l, x - 1)); |
87 | if (x < r) S.insert(make_pair(x + 1, r)); |
88 | add(l, x, x, r, i - q); |
89 | } else { |
90 | it = S.lower_bound(make_pair(x + 1, 0)), l = r = x, ti = it, ti--; |
91 | if (it -> first == x + 1) r = it -> second, S.erase(it); |
92 | if (ti -> second == x - 1) l = ti -> first, S.erase(ti); |
93 | S.insert(make_pair(l, r)); |
94 | add(l, x, x, r, q - i); |
95 | } |
96 | a[x] ^= 1; |
97 | } else { |
98 | scanf("%d %d", &x, &y), y--, ++m; |
99 | bool flag = false; |
100 | it = S.lower_bound(make_pair(x + 1, 0)), it--; |
101 | if (it -> second >= y) ans[m] = i - q, flag = true; |
102 | add_ev(2, x, y, m); |
103 | } |
104 | } |
105 | cdq(1, tot); |
106 | for (int i = 1; i <= m; i++) { |
107 | printf("%lld\n", ans[i]); |
108 | } |
109 | return 0; |
110 | } |