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(nlog2n+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]); 
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章