BZOJ-4012 开店(动态树分治)

传送门:BZOJ-4012

题意:给定一棵树,每个点有一个颜色,多次询问颜色在[l,r]区间内的所有点与某个点之间的距离之和

又是一个数组开小T到怀疑人生系列。。(我怎么总是管不住这双手root数组只开MX呢)

一个经典的动态树分治题目,用树状数组保存每种颜色的节点到该层子树的根节点的距离,然后一层一层计算就行。

= =本来应该是这样的,后来看了下发现颜色有n种,n^2logn的树状数组肯定爆内存啊。

后来想了下,明明只要记录区间内的值,用个vector弄下前缀和不就行了吗。。。(哈希都省去了

#include<cstdio>
#include<string.h>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int MX = 150005;
const int MXM = MX * 40;
const int inf = 0x3f3f3f3f;

struct Edge {
    int v, w, nxt;
} E[MX * 2];
int head[MX], tot;
struct node {
    int rt, subrt, nxt;
    LL dis;
} root[MXM];
struct p {
    int x;
    LL y, cnt;
    p(int x = 0, LL y = 0, LL cnt = 0): x(x), y(y), cnt(cnt) {}
    bool operator<(const p& _A)const {
        return x < _A.x;
    }
};
int fir[MX], rear;
int a[MX], vis[MX], sz[MX], id[MX];
int n, Root, Maxv, cnt;
vector<p>T[MX << 2], tmp;
void init() {
    memset(head, -1, sizeof(head));
    memset(fir, -1, sizeof(fir));
    memset(vis, 0, sizeof(vis));
    tot = rear = cnt = 0;
}
void add_edge(int u, int v, int w) {
    E[tot].v = v;
    E[tot].w = w;
    E[tot].nxt = head[u];
    head[u] = tot++;
}
void add_root(int u, int rt, int subrt, LL dis) {
    root[rear].rt = rt;
    root[rear].subrt = subrt;
    root[rear].dis = dis;
    root[rear].nxt = fir[u];
    fir[u] = rear++;
}
void dfs_size(int u, int fa) {
    sz[u] = 1;
    for (int i = head[u]; ~i; i = E[i].nxt) {
        int v = E[i].v;
        if (vis[v] || v == fa) continue;
        dfs_size(v, u);
        sz[u] += sz[v];
    }
}
void dfs_root(int u, int fa, int rt) {
    if (max(sz[u], sz[rt] - sz[u]) < Maxv) {
        Maxv = max(sz[u], sz[rt] - sz[u]);
        Root = u;
    }
    for (int i = head[u]; ~i; i = E[i].nxt) {
        int v = E[i].v;
        if (vis[v] || v == fa) continue;
        dfs_root(v, u, rt);
    }
}
p arr[MX];
void cal(int u) {
    if (T[u].empty()) return;
    sort(T[u].begin(), T[u].end());
    int sz = 0;
    arr[++sz] = p(-1, 0, 0);
    for (int i = 0; i < T[u].size(); i++) {
        if (T[u][i].x != arr[sz].x) {
            arr[++sz].x = T[u][i].x;
            arr[sz].y = arr[sz - 1].y + T[u][i].y;
            arr[sz].cnt = arr[sz - 1].cnt + 1;
        } else {
            arr[sz].y += T[u][i].y;
            arr[sz].cnt++;
        }
    }
    T[u].resize(sz);
    for (int i = 1; i <= sz; i++) T[u][i - 1] = arr[i];
}
void dfs_tree(int u, int fa, int rt, int subrt, LL dir) {
    add_root(u, rt, subrt, dir);
    T[rt].push_back(p(a[u], dir, 1));
    T[subrt].push_back(p(a[u], dir, 1));
    for (int i = head[u]; ~i; i = E[i].nxt) {
        int v = E[i].v;
        if (vis[v] || v == fa) continue;
        dfs_tree(v, u, rt, subrt, dir + E[i].w);
    }
}
void dfs(int u) {
    Root = 0; Maxv = n;
    dfs_size(u, -1); dfs_root(u, -1, u);
    vis[Root] = 1; id[Root] = ++cnt;
    T[cnt].clear();
    T[cnt].push_back(p(a[Root], 0, 1));
    add_root(Root, id[Root], 0, 0);
    for (int i = head[Root]; ~i; i = E[i].nxt) {
        int v = E[i].v;
        if (vis[v]) continue;
        T[++cnt].clear();
        dfs_tree(v, Root, id[Root], cnt, E[i].w);
        cal(cnt);
    }
    cal(id[Root]);
    for (int i = head[Root]; ~i; i = E[i].nxt) {
        int v = E[i].v;
        if (vis[v]) continue;
        dfs(v);
    }
}
p Query(int rt, int l, int r) {
    if (T[rt].empty()) return p();
    int p1 = lower_bound(T[rt].begin(), T[rt].end(), p(l)) - T[rt].begin() - 1;
    int p2 = upper_bound(T[rt].begin(), T[rt].end(), p(r)) - T[rt].begin() - 1;
    return p(0, T[rt][p2].y - T[rt][p1].y, T[rt][p2].cnt - T[rt][p1].cnt);
}
LL query(int u, int l, int r) {
    LL ret = 0;
    for (int i = fir[u]; ~i; i = root[i].nxt) {
        int rt = root[i].rt, subrt = root[i].subrt, dis = root[i].dis;
        p p1 = Query(rt, l, r);
        p p2 = Query(subrt, l, r);
        ret += p1.y - p2.y + (p1.cnt - p2.cnt) * dis;
    }
    return ret;
}
int main() {
    int m, A;
    //freopen("in.txt", "r", stdin);
    scanf("%d%d%d", &n, &m, &A);
    init();
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    for (int i = 1, u, v, w; i < n; i++) {
        scanf("%d%d%d", &u, &v, &w);
        add_edge(u, v, w); add_edge(v, u, w);
    }
    dfs(1);
    LL ans = 0;
    for (int i = 1, u, l, r; i <= m; i++) {
        scanf("%d%d%d", &u, &l, &r);
        l = (l + ans) % A;
        r = (r + ans) % A;
        if (l > r) swap(l, r);
        printf("%lld\n", ans = query(u, l, r));
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值