求助树链剖分

P3038 [USACO11DEC] Grass Planting G

Weight_of_the_Soul @ 2022-09-28 22:39:30

样例过不去,如果答案减一则有 39 pts,其余全 WA。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;

int rd() {
    int x = 0, w = 1;
    char c = getchar();

    while(c < '0' || c > '9') {
        if(c == '-') w = -1;
        c = getchar();
    }

    while(c >= '0' && c <= '9') {
        x = x * 10 + (c - '0');
        c = getchar();
    }

    return x * w;
}

const int N = 1e5 + 5;

int n, m;

struct Graph {
    int v, nxt;
}e[N << 1];

int lk[N], ltp;
int dfn[N], son[N], fa[N], siz[N], dep[N];
int rk[N], cnt;
int top[N];
int a[N];

void ins(int u, int v) {
    e[++ltp] = (Graph) {v, lk[u]};
    lk[u] = ltp;
}

void dfs1(int u) {
    siz[u] = 1;
    son[u] = -1;

    for(int i = lk[u]; i; i = e[i].nxt) {
        int v = e[i].v;
        if(!dep[v]) {
            dep[v] = dep[u] + 1;
            fa[v] = u;
            a[v] = 1;
            dfs1(v);

            siz[u] += siz[v];
            if(son[u] == -1 || siz[son[u]] < siz[v])
                son[u] = v;
        }
    }
}

void dfs2(int u, int t) {
    top[u] = t;
    ++cnt;
    dfn[u] = cnt;
    rk[cnt] = u;
    if(son[u] == -1) return ;
    dfs2(son[u], t);

    for(int i = lk[u]; i; i = e[i].nxt) {
        int v = e[i].v;
        if(v != son[u] && v != fa[u])
            dfs2(v, v);
    }
}

struct Tree {
    int l, r;
    int dat, tag;

    #define l(x) tr[x].l
    #define r(x) tr[x].r
    #define dat(x) tr[x].dat
    #define tag(x) tr[x].tag
    #define ls (p << 1)
    #define rs (ls | 1)

} tr[N << 3];

void build(int p, int l, int r) {
    l(p) = l, r(p) = r;
    if(l == r) {
        dat(p) = a[l];
        return ;
    }

    int mid = (r - l) / 2 + l;
    build(ls, l, mid), build(rs, mid + 1, r);
    dat(p) = dat(ls) + dat(rs);
}

void spr(int p) {
    if(tag(p) != 0) {
        tag(ls) += tag(p);
        tag(rs) += tag(p);
        dat(ls) += tag(p) * (r(ls) - l(ls) + 1);
        dat(rs) += tag(p) * (r(rs) - l(rs) + 1);
        tag(p) = 0;
    }
}

void change(int p, int l, int r, int del) {
    if(l <= l(p) && r >= r(p)) {
        dat(p) += del * (r(p) - l(p) + 1);
        tag(p) += del;
        return ;
    }

    spr(p);

    int mid = (r(p) - l(p)) / 2 + l(p);
    if(l <= mid) change(ls, l, r, del);
    if(r > mid) change(rs, l, r, del);

    dat(p) = dat(ls) + dat(rs);
}

int ask(int p, int l, int r) {
    if(l <= l(p) && r >= r(p)) return dat(p);

    spr(p);

    int res = 0;
    int mid = (r(p) - l(p)) / 2 + l(p);

    if(l <= mid) res += ask(ls, l, r);
    if(r > mid) res += ask(rs, l, r);

    dat(p) = dat(ls) + dat(rs);
    return res;
}

int main() {
    n = rd(), m = rd();
    for(int i = 1; i < n; ++i) {
        int u = rd(), v = rd();
        ins(u, v);
        ins(v, u);
    }

    dep[1] = 1;
    dfs1(1);
    dfs2(1, 1);

    build(1, 1, n);

    while(m--) {
        char opt;
        cin >> opt;
        int u = rd(), v = rd();
        if(opt == 'P') {
            while(top[u] != top[v]) {
                if(dep[top[u]] < dep[top[v]]) swap(u, v);
                change(1, dfn[top[u]], dfn[u], 1);
                u = fa[top[u]];
            }

            if(dep[u] > dep[v]) swap(u, v);
            change(1, dfn[u], dfn[v], 1);
            change(1, dfn[u], dfn[u], -1);
        } else {
            int ans = 0;
            while(top[u] != top[v]) {
                if(dep[top[u]] < dep[top[v]]) swap(u, v);
                ans += ask(1, dfn[top[u]], dfn[u]);
                u = fa[top[u]];
            }

            if(dep[u] > dep[v]) swap(u, v);
            ans += ask(1, dfn[u], dfn[v]);
            ans -= ask(1, dfn[u], dfn[u]);

            printf("%d\n", ans);
        }
    }
    return 0;
}

by Weight_of_the_Soul @ 2022-09-28 22:39:48

@Ptilopsis_w


by Ptilopsis_w @ 2022-09-29 05:44:41

建议remake(


by C_liar @ 2022-09-29 07:35:04

初始边权均为 0


by C_liar @ 2022-09-29 07:42:44

还有建树时,dat(p) = a[l] 是不对的,应该选(dfn 对应的节点的编号)的权值,个人习惯用 id 数组表示。

线段树上的节点相当于 dfn 值,直接这样会出问题(曾经因为这个wa了好多题),要写成 dat(p) = a[id[l]]


by C_liar @ 2022-09-29 07:47:10

(哦,你用 rk 数组表示了

不管怎样吧,改好了。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;

int rd() {
    int x = 0, w = 1;
    char c = getchar();

    while(c < '0' || c > '9') {
        if(c == '-') w = -1;
        c = getchar();
    }

    while(c >= '0' && c <= '9') {
        x = x * 10 + (c - '0');
        c = getchar();
    }

    return x * w;
}

const int N = 1e5 + 5;

int n, m;

struct Graph {
    int v, nxt;
}e[N << 1];

int lk[N], ltp;
int dfn[N], son[N], fa[N], siz[N], dep[N];
int rk[N], cnt;
int top[N];
int a[N];

void ins(int u, int v) {
    e[++ltp] = (Graph) {v, lk[u]};
    lk[u] = ltp;
}

void dfs1(int u) {
    siz[u] = 1;
    son[u] = -1;

    for(int i = lk[u]; i; i = e[i].nxt) {
        int v = e[i].v;
        if(!dep[v]) {
            dep[v] = dep[u] + 1;
            fa[v] = u;
            a[v] = 0;/**/
            dfs1(v);

            siz[u] += siz[v];
            if(son[u] == -1 || siz[son[u]] < siz[v])
                son[u] = v;
        }
    }
}

void dfs2(int u, int t) {
    top[u] = t;
    ++cnt;
    dfn[u] = cnt;
    rk[cnt] = u;
    if(son[u] == -1) return ;
    dfs2(son[u], t);

    for(int i = lk[u]; i; i = e[i].nxt) {
        int v = e[i].v;
        if(v != son[u] && v != fa[u])
            dfs2(v, v);
    }
}

struct Tree {
    int l, r;
    int dat, tag;

    #define l(x) tr[x].l
    #define r(x) tr[x].r
    #define dat(x) tr[x].dat
    #define tag(x) tr[x].tag
    #define ls (p << 1)
    #define rs (ls | 1)

} tr[N << 3];

void build(int p, int l, int r) {
    l(p) = l, r(p) = r;
    if(l == r) {
        dat(p) = a[rk[l]];/**/
        return ;
    }

    int mid = (r - l) / 2 + l;
    build(ls, l, mid), build(rs, mid + 1, r);
    dat(p) = dat(ls) + dat(rs);
}

void spr(int p) {
    if(tag(p) != 0) {
        tag(ls) += tag(p);
        tag(rs) += tag(p);
        dat(ls) += tag(p) * (r(ls) - l(ls) + 1);
        dat(rs) += tag(p) * (r(rs) - l(rs) + 1);
        tag(p) = 0;
    }
}

void change(int p, int l, int r, int del) {
    if(l <= l(p) && r >= r(p)) {
        dat(p) += del * (r(p) - l(p) + 1);
        tag(p) += del;
        return ;
    }

    spr(p);

    int mid = (r(p) - l(p)) / 2 + l(p);
    if(l <= mid) change(ls, l, r, del);
    if(r > mid) change(rs, l, r, del);

    dat(p) = dat(ls) + dat(rs);
}

int ask(int p, int l, int r) {
    if(l <= l(p) && r >= r(p)) return dat(p);

    spr(p);

    int res = 0;
    int mid = (r(p) - l(p)) / 2 + l(p);

    if(l <= mid) res += ask(ls, l, r);
    if(r > mid) res += ask(rs, l, r);

    dat(p) = dat(ls) + dat(rs);
    return res;
}

int main() {
    n = rd(), m = rd();
    for(int i = 1; i < n; ++i) {
        int u = rd(), v = rd();
        ins(u, v);
        ins(v, u);
    }

    dep[1] = 1;
    dfs1(1);
    dfs2(1, 1);

    build(1, 1, n);

    while(m--) {
        char opt;
        cin >> opt;
        int u = rd(), v = rd();
        if(opt == 'P') {
            while(top[u] != top[v]) {
                if(dep[top[u]] < dep[top[v]]) swap(u, v);
                change(1, dfn[top[u]], dfn[u], 1);
                u = fa[top[u]];
            }

            if(dep[u] > dep[v]) swap(u, v);
            change(1, dfn[u], dfn[v], 1);
            change(1, dfn[u], dfn[u], -1);
        } else {
            int ans = 0;
            while(top[u] != top[v]) {
                if(dep[top[u]] < dep[top[v]]) swap(u, v);
                ans += ask(1, dfn[top[u]], dfn[u]);
                u = fa[top[u]];
            }

            if(dep[u] > dep[v]) swap(u, v);
            ans += ask(1, dfn[u], dfn[v]);
            ans -= ask(1, dfn[u], dfn[u]);
            /*直接这样就行:ask(1, dfn[u]+1, dfn[v]) 不过线段树代码中要处理一些 l>r 的情况*/

            printf("%d\n", ans);
        }
    }
    return 0;
}

by C_liar @ 2022-09-29 07:48:46

QwQ,%东区大佬


by Weight_of_the_Soul @ 2022-09-29 22:04:24

@C_liar tql,thx


|