KTU Programming Camp (Day 3)
A. Queries
维护一个序列,要求支持单点修改和查询区间异或和之和
线段树
对于每一个二进制位单独考虑。考虑第 \(b\) 位的异或前缀和 \(S\),则区间 \([l, r]\) 内的异或和为 \(S_r \oplus S_{l - 1}\)。若结果不为零,则 \(S_r\) 和 \(S_{l - 1}\) 一个为 \(0\) 一个为 \(1\)。设区间 \([l - 1, r]\) 内所有位置的第 \(b\) 位的异或前缀和中,\(0\) 的个数为 \(Z\)、\(1\) 的个数为 \(O\),则对于 \([l, r]\) 所有子区间的异或和之和的贡献为 \(2^bZO\)。
故对于每一位建立一颗线段树,维护区间内 \(1\) 的个数,\(0\) 的个数可以计算得出。修改 \(a_i\) 后,若第 \(b\) 位发生了改变,则相当于这一位异或上了 \(1\),将导致 \([i, n]\) 内所有位置第 \(b\) 位的异或前缀和异或上 \(1\),即 \(1\) 和 \(0\) 互换。
#include <stdio.h>
const int MAXN = 100010, MOD = 4001;
int n, m, a[MAXN], s[MAXN], cnt[MAXN << 2][10]; // cnt 为区间内 1 的个数
bool tag[MAXN << 2][10];
void Build(int k, int l, int r) {
if (l == r) {
for (int i = 0; i < 10; ++i) {
cnt[k][i] = (s[l] >> i) & 1;
}
return;
}
int mid = (l + r) >> 1;
Build(k << 1, l, mid);
Build(k << 1 | 1, mid + 1, r);
for (int i = 0; i < 10; ++i) {
cnt[k][i] = cnt[k << 1][i] + cnt[k << 1 | 1][i];
}
}
void PushDown(int b, int k, int l, int r) {
int mid = (l + r) >> 1;
cnt[k << 1][b] = (mid - l + 1) - cnt[k << 1][b];
cnt[k << 1 | 1][b] = (r - mid) - cnt[k << 1 | 1][b];
tag[k << 1][b] ^= 1;
tag[k << 1 | 1][b] ^= 1;
tag[k][b] = 0;
}
void Modify(int b, int k, int l, int r, int x, int y) {
if (x <= l && r <= y) {
cnt[k][b] = (r - l + 1) - cnt[k][b];
tag[k][b] ^= 1;
return;
}
if (tag[k][b]) PushDown(b, k, l, r);
int mid = (l + r) >> 1;
if (x <= mid) Modify(b, k << 1, l, mid, x, y);
if (y > mid) Modify(b, k << 1 | 1, mid + 1, r, x, y);
cnt[k][b] = cnt[k << 1][b] + cnt[k << 1 | 1][b];
}
int Query(int b, int k, int l, int r, int x, int y) {
if (x <= l && r <= y) return cnt[k][b];
int mid = (l + r) >> 1;
if (tag[k][b]) PushDown(b, k, l, r);
int res = 0;
if (x <= mid) res += Query(b, k << 1, l, mid, x, y);
if (y > mid) res += Query(b, k << 1 | 1, mid + 1, r, x, y);
return res;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
s[i] = s[i - 1] ^ a[i];
}
Build(1, 0, n);
while (m--) {
int op, x, y;
scanf("%d%d%d", &op, &x, &y);
if (op == 1) {
for (int i = 0; i < 10; ++i) {
if (((y >> i) & 1) != ((a[x] >> i) & 1)) {
Modify(i, 1, 0, n, x, n);
}
}
a[x] = y;
} else {
int ans = 0;
for (int i = 0; i < 10; ++i) {
int temp = Query(i, 1, 0, n, x - 1, y);
ans = (ans + 1LL * (1 << i) * temp * (y - x + 2 - temp)) % MOD;
}
printf("%d\n", ans);
}
}
return 0;
}
B. Yet another vector problem
给定一个长度为 \(n\) 的序列 \(a_0, a_1, \dots , a_{n - 1}\),进行 \(Q\) 次询问。每次询问给定 \(p\) 和 \(q\),要求输出 \(\sum\limits_{i \mathrm{mod} p = q}a_i\)。
\(1 \leq n, Q \leq 10^5,\ 0 \leq p, q \leq n - 1\)
当 \(p > \sqrt{n}\) 时,直接遍历求和,复杂度为 \(O(\dfrac{n}{p}) < O(\sqrt{n})\);当 \(p \leq \sqrt{n}\) 时,先预处理出所有情况,每次询问时直接输出即可,预处理的复杂度为 \(O(n\sqrt{n})\)。总的复杂度为 \(O((n + Q)\sqrt{n})\)。
#include <stdio.h>
typedef long long ll;
const int MAXN = 100010;
int n, m, a[MAXN];
ll sum[400][400];
int main() {
scanf("%d %d", &n, &m);
for (int i = 0; i < n; ++i) {
scanf("%d", &a[i]);
}
for (int i = 1; i * i <= n; ++i) {
for (int rem = 0; rem < i; ++rem) {
for (int j = rem; j < n; j += i) {
sum[i][rem] += a[j];
}
}
}
while (m--) {
int p, q;
scanf("%d%d", &q, &p);
if ((ll)p * p <= n) {
printf("%lld\n", sum[p][q]);
} else {
ll ans = 0;
for (int i = q; i < n; i += p) {
ans += a[i];
}
printf("%lld\n", ans);
}
}
return 0;
}
C. Broken robot
BFS
#include <stdio.h>
#include <string.h>
#include <queue>
struct Node {
int x, y, dir;
Node() {}
Node(int _x, int _y, int _dir): x(_x), y(_y), dir(_dir) {}
};
const int MAXN = 210, INF = 0x3f3f3f3f;
const int dx[] = {1, 0, -1, 0};
const int dy[] = {0, 1, 0, -1};
int n, dist[MAXN][MAXN][4];
char s[MAXN][MAXN];
void BFS() {
memset(dist, 0x3f, sizeof(dist));
std::queue<Node> q;
q.push(Node(0, 0, 0));
dist[0][0][0] = 0;
while (!q.empty()) {
Node cur = q.front();
q.pop();
if (cur.x == n - 1 && cur.y == n - 1 && cur.dir == 0) return;
int tx = cur.x + dx[cur.dir], ty = cur.y + dy[cur.dir];
if (tx >= 0 && tx < n && ty >= 0 && ty < n && s[tx][ty] == '.' && dist[tx][ty][cur.dir] == INF) {
dist[tx][ty][cur.dir] = dist[cur.x][cur.y][cur.dir] + 1;
q.push(Node(tx, ty, cur.dir));
}
if (dist[cur.x][cur.y][(cur.dir + 1) % 4] == INF) {
dist[cur.x][cur.y][(cur.dir + 1) % 4] = dist[cur.x][cur.y][cur.dir] + 1;
q.push(Node(cur.x, cur.y, (cur.dir + 1) % 4));
}
}
}
int main() {
scanf("%d", &n);
for (int i = 0; i < n; ++i) {
scanf("%s", s[i]);
}
BFS();
printf("%d", dist[n - 1][n - 1][0]);
return 0;
}
E. What were those numbers?
#include <stdio.h>
typedef long long ll;
ll n, m, sum;
int main() {
scanf("%lld%lld%lld", &n, &sum, &m);
for (; m <= n; m <<= 1) {
if ((1 + m) * m / 2 > sum || (n + n - m + 1) * m / 2 < sum) continue;
ll d = sum - (1 + m) * m / 2;
ll inc = d / m;
ll rem = d - m * inc;
for (int i = 1; i <= m; ++i) {
int cur = i + inc;
if (m - i + 1 <= rem) cur++;
printf("%d", cur);
if (i < m) printf(" ");
}
return 0;
}
printf("IMPOSSIBLE");
return 0;
}
F. Old town
一个小镇有 \(n\) 个部分,由 \(m\) 条双向道路连接。其中有一些是古路,其余是新路。古路不可被拆除,新路则可以被修建和拆除。小镇的各个部分可以通过古路相互到达,且如果任何一条被拆除,都会有一些部分无法再通过古路相互到达。接下来将会有 \(Q\) 条新路被修建或拆除,请求出每次操作后的割边的数量。
\(1 \leq n, m, Q \leq 10^5\)
容易看出,古路构成了一颗树。在此基础上,每修建一条从 \(u\) 到 \(v\) 的新路,都将使得 \(u\) 到 \(v\) 的路径的上每一条边都多处在一个环中。如果一条边不在任何环中,那么这条边就是割边。维护每一条边所在的环的数量的最小值和最小值个数即可。
#include <stdio.h>
#include <vector>
#include <set>
#include <utility>
typedef std::pair<int, int> pii;
const int MAXN = 1e5 + 10;
int n, m, Q, num, dfn[MAXN], idx[MAXN], top[MAXN], size[MAXN], hson[MAXN], dep[MAXN], fa[MAXN];
int mn[MAXN << 2], cnt[MAXN << 2], tag[MAXN << 2];
std::vector<int> e[MAXN];
std::set<pii> newRd;
void Insert(int u, int v){
e[u].push_back(v);
e[v].push_back(u);
}
void DFS1(int u, int p) {
size[u] = 1;
fa[u] = p;
dep[u] = dep[p] + 1;
for (auto v : e[u]) {
if (v == p) continue;
DFS1(v, u);
size[u] += size[v];
if(size[v] > size[hson[u]]) hson[u] = v;
}
}
void DFS2(int u) {
dfn[u] = ++num;
idx[num] = u;
if (hson[u]) {
top[hson[u]] = top[u];
DFS2(hson[u]);
}
for (auto v : e[u]) {
if (v == fa[u] || v == hson[u]) continue;
top[v] = v;
DFS2(v);
}
}
void PushUp(int k) {
if (mn[k << 1] < mn[k << 1 | 1]) {
mn[k] = mn[k << 1];
cnt[k] = cnt[k << 1];
} else if (mn[k << 1] > mn[k << 1 | 1]) {
mn[k] = mn[k << 1 | 1];
cnt[k] = cnt[k << 1 | 1];
} else {
mn[k] = mn[k << 1];
cnt[k] = cnt[k << 1] + cnt[k << 1 | 1];
}
}
void Build(int k, int l, int r) {
if (l == r) {
if (l == 1) mn[k] = 0x3f3f3f3f;
cnt[k] = 1;
return;
}
int mid = (l + r) >> 1;
Build(k << 1, l, mid);
Build(k << 1 | 1, mid + 1, r);
PushUp(k);
}
void PushDown(int k, int l, int r) {
mn[k << 1] += tag[k];
tag[k << 1] += tag[k];
mn[k << 1 | 1] += tag[k];
tag[k << 1 | 1] += tag[k];
tag[k] = 0;
}
void Modify(int k, int l, int r, int x, int y, int val) {
if (x <= l && r <= y) {
mn[k] += val;
tag[k] += val;
return;
}
if (tag[k]) PushDown(k, l, r);
int mid = (l + r) >> 1;
if (x <= mid) Modify(k << 1, l, mid, x, y, val);
if (mid < y) Modify(k << 1 | 1, mid + 1, r, x, y, val);
PushUp(k);
}
void ChainAdd(int x, int y, int val) {
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) std::swap(x, y);
Modify(1, 1, n, dfn[top[x]], dfn[x], val);
x = fa[top[x]];
}
if (dep[x] > dep[y]) std::swap(x, y);
Modify(1, 1, n, dfn[x], dfn[y], val);
}
int LCA(int x, int y) {
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) std::swap(x, y);
x = fa[top[x]];
}
if (dep[x] > dep[y]) std::swap(x, y);
return x;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; ++i) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
if (u > v) std::swap(u, v);
if (w == 0) Insert(u, v);
else newRd.insert({u, v});
}
DFS1(1, 0);
top[1] = 1;
DFS2(1);
Build(1, 1, n);
for (auto it : newRd) {
ChainAdd(it.first, it.second, 1);
int z = LCA(it.first, it.second);
Modify(1, 1, n, dfn[z], dfn[z], -1);
}
printf("%d\n", mn[1] == 0 ? cnt[1] : 0);
scanf("%d", &Q);
while (Q--) {
int u, v;
scanf("%d%d", &u, &v);
if (u > v) std::swap(u, v);
if (newRd.count({u, v})) {
newRd.erase({u, v});
ChainAdd(u, v, -1);
int z = LCA(u, v);
Modify(1, 1, n, dfn[z], dfn[z], 1);
printf("%d\n", mn[1] == 0 ? cnt[1] : 0);
} else {
newRd.insert({u, v});
ChainAdd(u, v, 1);
int z = LCA(u, v);
Modify(1, 1, n, dfn[z], dfn[z], -1);
printf("%d\n", mn[1] == 0 ? cnt[1] : 0);
}
}
return 0;
}
H. Red and yellow
#include <stdio.h>
#include <math.h>
const double eps = 1e-6, PI = 3.14159265358;
int A, B, r, ans;
int main() {
scanf("%d%d%d", &A, &B, &r);
double thetaMin = 2 * asin(1.0 * r / (B + r));
double thetaMax = 2 * asin(1.0 * r / (A + r));
int l, r;
double res = 2 * PI / thetaMax;
if (abs(round(res) - res) < eps) l = round(res);
else l = ceil(res);
res = 2 * PI / thetaMin;
if (abs(round(res) - res) < eps) r = round(res);
else r = floor(res);
ans = r - l + 1;
printf("%d", ans);
return 0;
}
K. Many recursions
已知递归式 \(h(k) = 1 + \dfrac{a - k}{k + 1}h(k + 1)\),给定非负整数 \(a\),求 \(h(0)\)
把递归式展开,得