codeforces-191C (树链剖分+边的区间修改)

题意:给出一棵树,每次可以使任意两点间的路径的权值加1,最后求出每条边的权值

题解:可以作为基于边权的树链剖分的模板,有个技巧是用eg数组来将边的序号和点关联起来

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

int n, m;
int cnt, head[100010];
int eg[100010][2];

int e[100010];

int fa[100010];
int siz[100010];
int dep[100010];
int son[100010];
int top[100010];
int tid[100010], rnk[100010];

struct Edge{
    int v, next;
}edge[2*100010];

struct node{
    int l, r;
    int sum;
    int tag;
}s[4*100010];

void push(int k){
    if(s[k].tag){
        s[2*k].tag += s[k].tag;
        s[2*k+1].tag += s[k].tag;
        s[k].sum = (s[2*k].r-s[2*k].l+1)*s[2*k].tag + (s[2*k+1].r-s[2*k+1].l+1)*s[2*k+1].tag;
        s[k].tag = 0;
    }
}

void build(int l, int r, int k){
    s[k].l = l; s[k].r = r; s[k].tag = 0; s[k].sum = 0;
    if(l == r){
        return;
    }
    int mid = (l+r)>>1;
    build(l, mid, 2*k);
    build(mid+1, r, 2*k+1);
}

void update(int l, int r, int k){
    if(s[k].l == l && s[k].r == r){
        s[k].tag += 1;
        return;
    }
    push(k);
    int mid = (s[k].l+s[k].r)>>1;
    if(r<=mid) update(l, r, 2*k);
    else if(l>mid) update(l, r, 2*k+1);
    else{
        update(l, mid, 2*k);
        update(mid+1, r, 2*k+1);
    }
}

int query(int x, int k){
    if(s[k].l == x && s[k].r == x){
        return s[k].sum+(s[k].r-s[k].l+1)*s[k].tag;
    }
    push(k);
    int mid = (s[k].l+s[k].r)>>1;
    if(x<=mid) return query(x, 2*k);
    else return query(x, 2*k+1);
}



void init(){
    memset(head, -1, sizeof(head));
    memset(son, -1, sizeof(son));
    cnt = 0;
}

void addedge(int u, int v){
    edge[cnt].v = v;
    edge[cnt].next = head[u];
    head[u] = cnt++;
}

void dfs1(int u, int father, int depth){
    dep[u] = depth;
    fa[u] = father;
    siz[u] = 1;

    for(int i = head[u]; i!=-1; i = edge[i].next){
        int v = edge[i].v;
        if(v!=fa[u]){
            dfs1(v, u, depth+1);
            siz[u] += siz[v];
            if(son[u]!=-1 || siz[v]>siz[son[u]]){
                son[u] = v;
            }
        }
    }

}



void dfs2(int u, int t){
    top[u] = t;
    tid[u] = cnt;
    rnk[cnt] = u;
    cnt++;

    if(son[u] == -1){
        return;
    }

    dfs2(son[u], t);
    for(int i = head[u]; i!=-1; i = edge[i].next){
        int v = edge[i].v;
        if(v!=son[u] && v!=fa[u]){
            dfs2(v, v);
        }
    }

}

void tree_update(int u, int v){
    int f1 = top[u];
    int f2 = top[v];

    while(f1!=f2){
        if(dep[f1]<dep[f2]){
            swap(f1, f2);
            swap(u, v);
        }
        update(tid[f1], tid[u], 1);
        u = fa[f1]; f1 = top[u];
    }
    if(u == v) return;
    if(dep[u]>dep[v]) swap(u, v);
    update(tid[son[u]], tid[v], 1);

}

int main(){
    init();
    scanf("%d", &n);
    for(int i = 1; i<=n-1; i++){
        scanf("%d %d", &eg[i][0], &eg[i][1]);
        addedge(eg[i][0], eg[i][1]);
        addedge(eg[i][1], eg[i][0]);
    }

    for(int i = 1; i<=n-1; i++){
        if(dep[eg[i][0]] > dep[eg[i][1]]){
            swap(eg[i][0], eg[i][1]);
        }
    }

    dfs1(1, 0, 1);
    cnt = 0;
    dfs2(1, 1);

    build(0, cnt-1, 1);

    /*for(int i = 1; i<=5; i++){
        cout<<"i: "<<i<<" tid[i]: "<<tid[i]<<" siz[i]: "<<siz[i]<<" dep[i]: "<<dep[i]<<" fa[i]: "<<fa[i];
        cout<<" son[i] "<<son[i]<<" top[i]: "<<top[i]<<endl;
    }*/

    scanf("%d", &m);
    while(m--){
        int u, v;
        scanf("%d %d", &u, &v);
        tree_update(u, v);
    }

    for(int i = 1; i<=n-1; i++){
        if(dep[eg[i][0]] > dep[eg[i][1]]){
            swap(eg[i][0], eg[i][1]);
        }
    }


    for(int i = 1; i<=n-1; i++){
        printf("%d", query(tid[eg[i][1]], 1));
        if(i!=n-1) printf(" ");
        else printf("\n");
    }

    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值