[luogu P4211] [LNOI2014]LCA

[luogu P4211] [LNOI2014]LCA

题解

首先考虑把询问拆成两个前缀和
然后对于把把每个点一次插入,每次把那个点到根的所有点权值+1
询问就是z到根的路径权值和
树剖一下就做完了

#include<bits/stdc++.h>
#define mod 201314
#define ll long long
using namespace std;
const int N = 530005;
const int INF = 9999999;
struct A{
    int v, next;
}e[N];
int eid, p[N], w[N], fa[N], top[N], size[N], id[N], deep[N], sz;
int a[N], n, sum[N], fid[N], col[N];
void update(int u){
    sum[u] = sum[u<<1] + sum[u<<1|1];
}
void pushdown(int rt, int l, int r){ 
    int mid = (l + r) >> 1;
    sum[rt<<1] += (mid - l + 1) * col[rt];
    sum[rt<<1|1] += (r - mid) * col[rt];
    col[rt<<1] += col[rt];
    col[rt<<1|1] += col[rt];
    col[rt] = 0;
}
void Add(int l, int r, int L, int R, int rt, int o){ //printf("%d %d %d %d   %d %d\n", l, r, rt, o, L, R);
    pushdown(rt, l, r);
    if(L <= l && r <= R){
        sum[rt] += (r - l + 1) * o;
        col[rt] += o;
        return;
    }
    int mid = (l + r) >> 1,ret = 0;
    if(L <= mid) Add(l, mid, L, R, rt<<1, o);
    if(R > mid)  Add(mid + 1, r, L, R, rt<<1|1, o);
    update(rt);
}
int querys(int l, int r, int L, int R, int rt){
    pushdown(rt, l, r);
    if(L <= l && r <= R){
        return sum[rt];
    }
    int mid = (l + r) >> 1,ret = 0;
    if(L <= mid) ret += querys(l, mid, L, R, rt<<1);
    if(R > mid) ret += querys(mid + 1, r, L, R, rt<<1|1);
    return ret;
}
void init(){
    memset(p, -1, sizeof(p));
    eid = 0;
}
void add(int u, int v){
    e[eid].next = p[u];
    e[eid].v = v;
    p[u] = eid ++;
}
void dfs1(int u){ 
    size[u] = 1;
    for(int i = p[u]; i + 1; i = e[i].next){
        int v = e[i].v;
        if(fa[u] == v) continue;
        deep[v] = deep[u] + 1;
        fa[v] = u;
        dfs1(v);
        size[u] += size[v];
        if(size[v] > size[w[u]]) w[u] = v;	
    }
}
void dfs2(int u){ 
    id[u] = ++ sz;
    if(w[u]) top[w[u]]=top[u], dfs2(w[u]);
    for(int i = p[u]; i + 1; i = e[i].next){
        int v = e[i].v;
        if(fa[u] == v || v == w[u]) continue;
        top[v] = v;
        dfs2(v);
    }	
}
int query(int u, int v){if(!u) return 0;
    int ret = 0;
    while(top[u] != top[v]){ 
        if(deep[top[u]] <= deep[top[v]]) swap(u, v);
        ret += querys(1, n, id[top[u]], id[u], 1);
        ret %= mod;
 		u = fa[top[u]];
    }
    if(deep[u] < deep[v]) swap(u, v);
    ret += querys(1, n, id[v], id[u], 1);
    return ret;
} 
void change(int u, int v, int o){// printf("%d*%d\n", u, v);
    if(!u) return;
    int ret = 0;
    while(top[u] != top[v]){ 
        if(deep[top[u]] <= deep[top[v]]) swap(u, v);
        Add(1, n, id[top[u]], id[u], 1, o);
 		u = fa[top[u]];
    }
    if(deep[u] < deep[v]) swap(u, v);
    Add(1, n, id[v], id[u], 1, o);
}
struct QQ{
    int l, z, id;
}q[N];
int cmp(QQ a, QQ b){
    return a.l < b.l;
}
int ans[N], F[N], m, tot;
int main(){
    init();
    scanf("%d%d", &n, &m);
    for(int i = 2; i <= n; i++){
        int v;
        scanf("%d", &v);
        v ++;
        add(v, i);
    }
    dfs1(1);dfs2(1);
//	printf("  size:");for(int i = 1 ; i <= n; i ++) printf("%d ", size[i]); printf("\n");
//	printf("father:");for(int i = 1 ; i <= n; i ++) printf("%d ", fa[i]); printf("\n");
//	printf("   top:");for(int i = 1 ; i <= n; i ++) printf("%d ", top[i]); printf("\n");
//	printf("  deep:");for(int i = 1 ; i <= n; i ++) printf("%d ", deep[i]); printf("\n");
//	printf("weight:");for(int i = 1 ; i <= n; i ++) printf("%d ", w[i]); printf("\n");
    for(int i = 1; i <= m; i ++){
        int u, v, z;
        scanf("%d%d%d", &u, &v, &z);
        u ++, v ++, z ++;
        q[++ tot].l = u - 1, q[tot].z = z, q[tot].id = i;
        q[++ tot].l = v, q[tot].z = z, q[tot].id = i;
    }
    sort(q + 1, q + 1 + tot, cmp);
    int j = 1;
    for(int i = 0; i <= n; i ++){
        change(1, i, 1);//printf("\n");
        for(;q[j].l == i;){
            if(!F[q[j].id]) ans[q[j].id] -= query(1, q[j].z), F[q[j].id] = 1;
            else ans[q[j].id] += query(1, q[j].z);
            j ++;
        }
    }
    for(int i = 1; i <= m; i ++) printf("%d\n", (ans[i] + mod) % mod);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值