1. 程式人生 > 實用技巧 >BZOJ 4012. [HNOI2015]開店

BZOJ 4012. [HNOI2015]開店

BZOJ 4012. [HNOI2015]開店

題目描述

詢問一個點到樹上其它點的距離,這些點滿足顏色屬於區間\([l,r]\).

多組詢問

解題思路

首先考慮離線的做法,把所有顏色離散化,然後掃描顏色序列,每掃到一個點就把它到根的貢獻算上,就是每個點都加上父邊長度的貢獻,這樣求答案的時候直接跳到根節點,得到的總的權值和,就是dep(lca(u,v))的和。

這樣再利用經典的樹上算距離的公式算一下就好。

但是這道題目要求強制線上,我們考慮變成主席樹。

乍一看做不了,因為是每次\(O(n)\) 個單點修改。所以我們要使用標記永久化,這樣只用修改\(O(log^2)\)個節點了。但是空間還是開不下。

注意到同一個版本會有許多重複開的新節點,這樣我們對每個節點打標記,如果上次修改這個節點的版本和此次相同,就不開新的節點,這樣可以卡很多空間。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 150011;
int n, Q, A;
int tot, rt[N], a[N], b[N], num;
int head[N], nex[N<<1], to[N<<1], wei[N<<1], size;
LL sumE[N], sumV[N], cntV[N], val[N], dis[N];
int id[N], dfn[N], son[N], sz[N], top[N], fa[N], cnt;
struct node{
    int ls, rs, cnt, tim;
    LL val;
}t[N*150];
void add(int x, int y, int z){
    to[++size] = y;
    nex[size] = head[x];
    head[x] = size;
    wei[size] = z;
}
void dfs(int u){
    sz[u] = 1;
    for(int i = head[u];i;i = nex[i]){
        int v = to[i];
        if(v == fa[u])continue;
        fa[v] = u; val[v] = wei[i];
        dis[v] = dis[u] + wei[i];
        dfs(v);
        sz[u] += sz[v];
        if(sz[v] > sz[son[u]])son[u] = v;
    }
}
void dfs(int u, int tp){
    top[u] = tp;
    dfn[u] = ++num;
    id[num] = u;
    if(son[u])dfs(son[u], tp);
    for(int i = head[u];i;i = nex[i]){
        int v = to[i];
        if(v == fa[u] || v == son[u])continue;
        dfs(v, v);
    }
}
void modi(int &p, int l, int r, int x, int y, int tim){
    int now = t[p].tim == tim ? p : ++tot;
    t[now] = t[p];
    t[now].tim = tim;
    p = now;
    t[p].val += sumE[min(y, r)] - sumE[max(l, x)-1];
    if(l >= x && r <= y){
        t[now].cnt++; 
        return ;
    }
    int mid = l + r >> 1;
    if(mid >= y)modi(t[p].ls, l, mid, x, y, tim);
    else if(mid < x)modi(t[p].rs, mid + 1, r, x, y, tim);
    else modi(t[p].ls, l, mid, x, y, tim), modi(t[p].rs, mid + 1, r, x, y, tim);
}
LL query(int R, int L, int l, int r, int x, int y){
    if(l > y || r < x)return 0;
    if(l >= x && r <= y){
        return t[R].val - t[L].val;
    }
    int mid = l + r >> 1;
    return query(t[R].ls, t[L].ls, l, mid, x, y) + query(t[R].rs, t[L].rs, mid + 1, r, x, y) + 1LL * (t[R].cnt - t[L].cnt) * (sumE[min(y, r)] - sumE[max(x, l)-1]);
}
bool cmp(int x, int y){
    return a[x] < a[y];
}
void jump(int x, int id){
    while(x){
        modi(rt[id], 1, n, dfn[top[x]], dfn[x], id);
        x = fa[top[x]];
    }
}
int main(){
    freopen("4012.in", "r", stdin);
    //freopen("4012.out", "w", stdout);
    cin>>n>>Q>>A;
    for(int i = 1;i <= n; i++){
        scanf("%d", &a[i]);
        b[i] = a[i];
    }
    sort(b + 1, b + 1 + n);
    cnt = unique(b + 1, b + 1 + n) - (b + 1);
    static int p[N];
    for(int i = 1;i <= n; i++){
        a[i] = lower_bound(b + 1, b + 1 + cnt, a[i]) - b;
        p[i] = i;
    }
    int u, v, w;
    for(int i = 1;i < n; i++){
        scanf("%d%d%d", &u, &v, &w);
        add(u, v, w); add(v, u, w);
    }
    dfs(1);
    dfs(1, 1);
    for(int i = 1;i <= n; i++){
        sumE[i] = sumE[i-1] + val[id[i]];
    }
    sort(p + 1, p + 1 + n, cmp);
    int pos = 1;
    for(int i = 1;i <= n; i++){
        int x = p[i];
        rt[a[x]] = rt[a[p[i-1]]];
        sumV[a[x]] += dis[x];
        cntV[a[x]]++;
        jump(x, a[x]);
    }
    for(int i = 1;i <= cnt; i++)sumV[i] += sumV[i-1], cntV[i] += cntV[i-1];
    int a, b;
    cin>>u>>a>>b;
    int ans = 0;
    for(int i = 1;i <= Q; i++){
        int L = min((a + ans) % A, (b + ans) % A);
        int R = max((a + ans) % A, (b + ans) % A);
        L = lower_bound(::b + 1, ::b + 1 + cnt, L) - ::b;
        R = upper_bound(::b + 1, ::b + 1 + cnt, R) - ::b - 1;
        if(L > R){
            ans = 0;
        }
        else{
            LL res = (cntV[R] - cntV[L-1]) * dis[u] + sumV[R] - sumV[L-1];
            int x = u;
            while(x){
                res -= 2ll * query(rt[R], rt[L-1], 1, n, dfn[top[x]], dfn[x]);
                x = fa[top[x]];
            }
            ans = res;
        }
        printf("%lld\n", ans);
        if(i < Q)scanf("%d%d%d", &u, &a, &b);
    }
    return 0;
}