【知识点】线段树相关算法

线段树合并:

一般是将若干棵权值线段树的信息整合到一棵权值线段树上。均摊复杂度$O(n\log{n})$。

同时遍历两棵线段树,若某一边没有节点则直接返回另一边的节点,否则继续遍历直到$l=r$。

#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 to[maxn<<1],nxt[maxn<<1],hd[maxn],F[maxn][20];
int tr[maxn<<4],mx[maxn<<4],ls[maxn<<4],rs[maxn<<4];
int ans[maxn],rt[maxn],cnt,tot,dep[maxn],N=100000;

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 dfs(int u,int fa){
    F[u][0]=fa,dep[u]=dep[fa]+1;
    for(int i=1;i<20;i++) F[u][i]=F[F[u][i-1]][i-1];
    for(int i=hd[u];i;i=nxt[i]){
        int v=to[i];
        if(v!=fa) dfs(v,u);
    }
}

inline int lca(int u,int v){
    if(dep[u]>dep[v]) swap(u,v);
    for(int i=19;i>=0;i--)
        if(dep[F[v][i]]>=dep[u])
            v=F[v][i];
    if(u==v) return u;
    for(int i=19;i>=0;i--)
        if(F[u][i]!=F[v][i])
            u=F[u][i],v=F[v][i];
    return F[u][0];
}

inline void pushup(int k){
    if(tr[ls[k]]>=tr[rs[k]]) tr[k]=tr[ls[k]],mx[k]=mx[ls[k]];
    else tr[k]=tr[rs[k]],mx[k]=mx[rs[k]]; 
}

inline int add(int x,int y,int l,int r,int k){
    if(!k) k=++tot;
    if(l==r){tr[k]+=y,mx[k]=l;return k;}
    int mid=l+r>>1;
    if(x<=mid) ls[k]=add(x,y,l,mid,ls[k]); 
    else rs[k]=add(x,y,mid+1,r,rs[k]);
    pushup(k); return k;
}

inline int merge(int l,int r,int u,int v){
    if(!u || !v) return u|v;
    if(l==r){tr[u]+=tr[v],mx[u]=l;return u;}
    int mid=l+r>>1;
    ls[u]=merge(l,mid,ls[u],ls[v]);
    rs[u]=merge(mid+1,r,rs[u],rs[v]);
    pushup(u); return u;
}

inline void Dfs(int u,int fa){
    for(int i=hd[u];i;i=nxt[i]){
        int v=to[i];
        if(v==fa) continue;
        Dfs(v,u);
        rt[u]=merge(1,N,rt[u],rt[v]);
    }
    ans[u]=(tr[rt[u]]==0)?0:mx[rt[u]];
}

int main(){
    int n=read(),m=read();
    for(int i=1;i<n;i++){
        int u=read(),v=read();
        addedge(u,v);
    }
    dfs(1,0);
    for(int i=1;i<=m;i++){
        int u=read(),v=read(),w=read(),l=lca(u,v);
        rt[u]=add(w,1,1,N,rt[u]);
        rt[v]=add(w,1,1,N,rt[v]);
        rt[l]=add(w,-1,1,N,rt[l]);
        rt[F[l][0]]=add(w,-1,1,N,rt[F[l][0]]);
    }
    Dfs(1,0);
    for(int i=1;i<=n;i++) cout<<ans[i]<<endl;
    return 0;
}
线段树合并

 

线段树分治:

大概是给你若干个只在时间$[l,r]$内生效的操作,求每个时间的状态。

那么以时间为下标建立线段树,把每个操作视为区间操作。

一般会写成标记永久化的形式。复杂度$O(n\log{n})$。

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

using namespace std;
ll ans[maxn],tr[maxn<<2],num[maxn<<2];
ll L[maxn],R[maxn],val[maxn],n,mod;

inline ll read(){
    ll 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 build(int l,int r,int k){
    tr[k]=1; if(l==r) return;
    int mid=l+r>>1;
    build(l,mid,k<<1),build(mid+1,r,k<<1|1);
}

inline void mul(ll x,ll y,ll z,ll l,ll r,ll k){
    if(x<=l && r<=y){tr[k]=tr[k]*z%mod;return;}
    ll mid=l+r>>1;
    if(x<=mid) mul(x,y,z,l,mid,k<<1);
    if(y>mid) mul(x,y,z,mid+1,r,k<<1|1);
}

inline void solve(ll l,ll r,ll v,ll k){
    num[k]=tr[k]*v%mod;
    if(l==r){ans[l]=num[k];return;}
    ll mid=l+r>>1;
    solve(l,mid,num[k],k<<1);
    solve(mid+1,r,num[k],k<<1|1); 
}

int main(){
    ll T=read();
    while(T--){
        n=read(),mod=read();
        for(ll i=1;i<=n;i++){
            ll op=read(),m=read();
            if(op==1) L[i]=i,R[i]=n,val[i]=m;
            else L[i]=R[i]=val[i]=0,R[m]=i-1;
        }
        build(1,n,1);
        for(ll i=1;i<=n;i++){
            //cout<<L[i]<<" "<<R[i]<<" "<<val[i]<<endl;
            if(val[i]) mul(L[i],R[i],val[i],1,n,1);
        }
        solve(1,n,1,1);
        for(ll i=1;i<=n;i++)
            printf("%lld\n",ans[i]);
    } 
    return 0;
}
线段树分治

 

posted @ 2020-06-08 20:42  Fugtemypt  阅读(182)  评论(0编辑  收藏  举报