[Vani有约会] 雨天的尾巴

题意:

有一棵n个点的树,m次操作,每次操作给路径$(u,v)$上每个点发一个类型为w的物品。

在所有操作后请你求出每个点个数最多的物品类型。

$n,m,wleq 10^{5}$。

题解:

两种做法,树链剖分和线段树合并。下一篇写线段树合并。

树链剖分基本就是把序列问题上树,于是考虑序列怎么做,直接把区间加改成差分再维护一棵权值线段树即可。

复杂度$O(nlog^{2}{n})$。注意那个w不一定小于n,这个玩意差点把我坑死。

套路:

  • 树上路径相关问题:如果序列可做$ ightarrow$树剖上树。

代码:

#include<bits/stdc++.h>
#define maxn 1000005
#define maxm 500005
#define inf 0x7fffffff
#define ll long long
#define rint register int
#define debug(x) cerr<<#x<<": "<<x<<endl
#define fgx cerr<<"--------------"<<endl
#define dgx cerr<<"=============="<<endl

using namespace std;
int top[maxn],E[maxn],id[maxn],rt[maxn];
int to[maxn<<1],nxt[maxn<<1],hd[maxn],cnt;
int F[maxn],dep[maxn],siz[maxn],son[maxn];
int tr[maxn<<2],mx[maxn<<2],ans[maxn]; 
vector<int> vc[maxn];

inline int read(){
    int x=0,f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}

inline void addedge(int u,int v){
    to[++cnt]=v,nxt[cnt]=hd[u],hd[u]=cnt;
    to[++cnt]=u,nxt[cnt]=hd[v],hd[v]=cnt;
}

inline void dfs1(int u,int fa){
    F[u]=fa,dep[u]=dep[fa]+1,siz[u]=1;
    for(int i=hd[u];i;i=nxt[i]){
        int v=to[i];
        if(v==fa) continue;
        dfs1(v,u),siz[u]+=siz[v];
        if(siz[v]>siz[son[u]]) son[u]=v;
    }
}

inline void dfs2(int u,int tp){
    E[++E[0]]=u,id[u]=E[0],top[u]=tp;
    if(son[u]) dfs2(son[u],tp);
    for(int i=hd[u];i;i=nxt[i]){
        int v=to[i];
        if(v!=F[u] && v!=son[u]) dfs2(v,v);
    }
}

inline void solve(int u,int v,int w){
    while(top[u]!=top[v]){
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        int st=id[top[u]],ed=id[u];
        vc[st].push_back(w),vc[ed+1].push_back(-w);    
        u=F[top[u]];
    }
    if(dep[u]>dep[v]) swap(u,v);
    int st=id[u],ed=id[v];
    vc[st].push_back(w),vc[ed+1].push_back(-w);
}

inline void add(int x,int y,int l,int r,int k){
    if(l==r){tr[k]+=y;return;}
    int mid=l+r>>1;
    if(x<=mid) add(x,y,l,mid,k<<1);
    else add(x,y,mid+1,r,k<<1|1);
    if(tr[k<<1]>=tr[k<<1|1]) tr[k]=tr[k<<1],mx[k]=mx[k<<1];
    else tr[k]=tr[k<<1|1],mx[k]=mx[k<<1|1];
}

inline void build(int l,int r,int k){
    if(l==r){mx[k]=l;return;}
    int mid=l+r>>1;
    build(l,mid,k<<1);
    build(mid+1,r,k<<1|1);
}

int main(){
    int n=read(),m=read();
    for(int i=1;i<n;i++){
        int u=read(),v=read();
        addedge(u,v);
    }
    dfs1(1,0),dfs2(1,1);
    for(int i=1;i<=m;i++){
        int u=read(),v=read(),w=read();
        solve(u,v,w);
    } 
    build(1,100000,1);
    for(int i=1;i<=n;i++){
        for(int j=0;j<vc[i].size();j++){
            if(vc[i][j]>0) add(vc[i][j],1,1,100000,1);
            else add(-vc[i][j],-1,1,100000,1);
        }
        ans[E[i]]=(tr[1]==0)?0:mx[1];
    }
    for(int i=1;i<=n;i++)
        cout<<ans[i]<<endl;
    return 0;
}
树剖写法
原文地址:https://www.cnblogs.com/YSFAC/p/13066965.html