zoukankan      html  css  js  c++  java
  • bzoj 4390(树上差分)

    传送门

    题意:

    给你一颗有nn个结点的树以及mm个路径。对于每一个路径pathipath_i,代表着你将会从uiu_i走到viv_i。现在问你,你走完着mm个路径后,在这nn个结点中经过的最多的次数。

    题目分析:

    首先,如果我们用dfsdfs在树上暴力去跑的话,显然时间肯定是接受不了的。因此我们需要考虑一种较为优美的算法。

    我们发现,经过每一个路径pathipath_i,本质上是使路径pathipath_i上的所有的点权+1+1,如果这个问题是在序列上的话,我们直接就可以用线段树或差分树状数组去做。而正因为这个问题是在树上进行的,因此为了简化问题,我们则需要将树形的结构转化成链式结构。

    因此根据转化的不同,该问题可以通过树链剖分或者树上差分去解决。

    而显然树上差分相对来说比较简单。

    我们只需要令cnt[ui]+1cnt[u_i]+1cnt[vi]+1cnt[v_i]+1,并且令cnt[ lca[ui,vi] ]1cnt[~lca[u_i,v_i]~]-1cnt[ fa[ lca[ui,vi] ] ]1cnt[~fa[~lca[u_i,v_i]~] ~]-1即可。如果我们采用上述差分方法,在最后我们进行dfsdfs回溯的时候,当前结点uu就能够获得他的儿子们的权值。

    整体的时间复杂度:O(nlogn)mathcal{O}(nlogn)

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=500005;
    const int LOG=20;
    struct Node{
        int to,next;
    }q[maxn<<1];
    int head[maxn],cnt=0;
    void add_edge(int from,int to){
        q[cnt].to=to;
        q[cnt].next=head[from];
        head[from]=cnt++;
    }
    struct LCA{//倍增lca
        int anc[maxn][LOG],depth[maxn];
        void dfs(int x,int fa,int dis){
            anc[x][0]=fa;depth[x]=dis;
            for(int i=head[x];i!=-1;i=q[i].next){
                int to=q[i].to;
                if(to==fa) continue;
                dfs(to,x,dis+1);
            }
        }
        void init(int root,int n){
            dfs(root,-1,1);
            for(int j=1;j<LOG;j++){
                for(int i=1;i<=n;i++){
                    anc[i][j]=anc[ anc[i][j-1] ][j-1];
                }
            }
        }
        void swim(int &x,int h){
            for(int i=0;h>0;i++){
                if(h&1)
                    x=anc[x][i];
                h>>=1;
            }
        }
        int query(int x,int y){
            if(depth[x]<depth[y]) swap(x,y);
            swim(x,depth[x]-depth[y]);
            if(x==y) return x;
            for(int i=LOG-1;i>=0;i--){
                if(anc[x][i]!=anc[y][i]){
                    x=anc[x][i];
                    y=anc[y][i];
                }
            }
            return anc[x][0];
        }
    }lca;
    int Bit[maxn];
    void Search(int x,int fa){//最优一次dfs获取权值
        for(int i=head[x];i!=-1;i=q[i].next){
            int to=q[i].to;
            if(to==fa) continue;
            Search(to,x);
            Bit[x]+=Bit[to];
        }
    }
    int main()
    {
        int n,m;
        scanf("%d%d",&n,&m);
        memset(head,-1,sizeof(head));
        cnt=0;
        for(int i=1;i<n;i++){
            int from,to;
            scanf("%d%d",&from,&to);
            add_edge(from,to);
            add_edge(to,from);
        }
        lca.init(1,n);
        while(m--){
            int l,r;
            scanf("%d%d",&l,&r);
            Bit[l]++,Bit[r]++;//差分
            int x=lca.query(l,r);
            Bit[x]--;
            Bit[lca.anc[x][0]]--;
        }
        Search(1,-1);
        int maxx=0;
        for(int i=1;i<=n;i++){
            maxx=max(maxx,Bit[i]);
        }
        printf("%d
    ",maxx);
        return 0;
    }
    
  • 相关阅读:
    roundabout插件使用(3d旋转轮播图)兼容IE8
    css实现定高的元素在不定高的容器中水平垂直居中(兼容IE8及以上)
    jq点击小图 弹出大图(更新版)
    pc端页面在移动端显示问题
    swiper横向轮播--3d
    swiper横向轮播(兼容IE8)
    windows 7安装apache
    从SDP中至少要看到那些东西?
    FS拓展设置
    Freeswitch 入门
  • 原文地址:https://www.cnblogs.com/Chen-Jr/p/11007151.html
Copyright © 2011-2022 走看看