Codeforces 191C (LCA+树上差分算法)

题面

传送门
题目大意:
给出一棵树,再给出k条树上的简单路径,求每条边被不同的路径覆盖了多少次

分析

解决这个问题的经典做法是树上差分算法
它的思想是把”区间”修改转化为左右端点的修改
在树上,每个节点初始权值为0,对于每条路径(x,y),我们令节点x的权值+1,节点y的权值-1,节点LCA(x,y)的权值-2
最后进行一次DFS,求出F[x]表示x为根的质数中各节点的权值之和,F[x]就是x与它的父节点之间的树边被覆盖的次数
用dfs序+ST表求LCA,时间复杂度 O ( n l o g 2 n + k )

代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#define maxn 100005
#define maxlog 32
using namespace std;
int n,k;
struct edge{
    int from;
    int to;
    int next;
    int index;
}E[maxn*2];
int head[maxn];
int size=0;
void add_edge(int u,int v,int id){
    size++;
    E[size].from=u;
    E[size].to=v;
    E[size].next=head[u];
    E[size].index=id;
    head[u]=size;
}
int dfn[maxn*2];
int used[maxn];
int fipos[maxn*2];
int deep[maxn*2];
int st[maxn*4][maxlog];
int cnt=0;
void get_dfn(int u,int d){
    dfn[++cnt]=u;
    if(fipos[u]==0){
        fipos[u]=cnt;
        deep[u]=cnt; 
    } 
    used[u]=1;
    for(int i=head[u];i;i=E[i].next){
        if(!used[E[i].to]){
            get_dfn(E[i].to,d+1);
            dfn[++cnt]=u;
        }
    }
} 
void st_init(){
    for(int i=1;i<=(n-1)*2;i++){
        st[i][0]=dfn[i];
    }
    for(int j=1;(1<<j)<=(n-1)*2;j++){
        for(int i=1;i<=(n-1)*2;i++){
            if(deep[st[i][j-1]]<deep[st[i+(1<<(j-1))][j-1]]) st[i][j]=st[i][j-1];
            else st[i][j]=st[i+(1<<(j-1))][j-1];
        }
    } 
}
int st_query(int l,int r){
    if(l>r) swap(l,r);
    int k=log2(r-l+1);
    if(deep[st[l][k]]<deep[st[r-(1<<k)+1][k]]) return st[l][k];
    else return st[r-(1<<k)+1][k];
}
int lca(int x,int y){
    int fx=fipos[x];
    int fy=fipos[y];
    return st_query(fx,fy);
}
int len[maxn];
int outlen[maxn];//按题目顺序输出 
void get_len(int u){
    used[u]=1;
    for(int i=head[u];i;i=E[i].next){
        if(!used[E[i].to]){
            get_len(E[i].to);
            len[u]+=len[E[i].to];
            outlen[E[i].index]+=len[E[i].to];
        }
    } 
}
int main(){
    int u,v;
    scanf("%d",&n);
    for(int i=1;i<=n-1;i++){
        scanf("%d %d",&u,&v);
        add_edge(u,v,i);
        add_edge(v,u,i);
    }
    get_dfn(1,0);
    st_init();
    scanf("%d",&k);
    for(int i=1;i<=k;i++){
        scanf("%d %d",&u,&v);
        //树上差分算法
        len[u]++;
        len[v]++;
        len[lca(u,v)]-=2;
    }
    memset(used,0,sizeof(used));
    get_len(1);
    for(int i=1;i<=n-1;i++) printf("%d ",outlen[i]); 
}

猜你喜欢

转载自blog.csdn.net/oier_forever/article/details/80951045