KTU Programming Camp (Day 3)

发布时间 2023-08-06 20:46:34作者: 东方澂

A. Queries

线段树

对于每一个二进制位单独考虑。考虑第 \(b\) 位的异或前缀和 \(S\),则区间 \([l, r]\) 内的异或和为 \(S_r \oplus S_{l - 1}\)。若结果不为零,则 \(S_r\)\(S_{l - 1}\) 一个为 \(0\) 一个为 \(1\)。设区间 \([l - 1, r]\) 内所有位置的异或前缀和中,\(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

\(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)\)
把递归式展开,得

\[\begin{aligned} h(0) &= 1 + a(1 + \dfrac{a - 1}{2}(1 + \dfrac{a - 2}{3}(\dotsm (1 + \dfrac{3}{a - 2}(1 + \dfrac{2}{a - 1}(1 + \dfrac{1}{a})))))) \\ &= 1 + a + \dfrac{a(a + 1)}{2} + \dfrac{a(a - 1)(a - 2)}{2 \times 3} + \dotsm + a + 1 \\ &= \mathrm{C}_a^0 + \mathrm{C}_a^1 + \mathrm{C}_a^2 + \mathrm{C}_a^3 + \dotsm + \mathrm{C}_a^{a - 1} + \mathrm{C}_a^a \\ &= 2^a \end{aligned} \]