zoukankan      html  css  js  c++  java
  • 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]); 
    }
  • 相关阅读:
    DMA详解
    Python实现爬虫设置代理IP和伪装成浏览器的方法(转载)
    Python3 简单验证码识别思路及实例
    Python3 比较两个图片是否类似或相同
    python3 验证码图片切割
    python3 验证码去噪
    Python3 OpenCV应用
    python3 IEDriver抓取时报数据
    Impala 数值函数大全(转载)
    java 获取指定日前的前一天
  • 原文地址:https://www.cnblogs.com/birchtree/p/9845833.html
Copyright © 2011-2022 走看看