树链剖分与例题

为了解决树上子树修改的问题,我们引入了树的dfs序:在树上dfs时,按dfs到的前后顺序给每个点设置dfn[x]表示x是第dfn[x]到达的

由于子树内的点的dfn连续,我们将点权按照dfn顺序放在序列上,于是子树x全体修改等价于对区间[dfn[x],dfn[x]+siz[x]-1]进行修改,例题如下:

jzyz511 工资查询

区间加减,单点询问,可以用树状数组维护差分数组,则问题转变成单点修改,求前缀和,可以用树状数组解决。用线段树也可,因为马上要用到很多次线段树。

有一个小坑点是修改工资时不给自己修改,只给子树内其他点修改,因此需要特判叶子的情况,修改区间也从[dfn[x],dfn[x]+siz[x]-1]变为了[dfn[x]+1,dfn[x]+siz[x]-1]

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=500010;
int lowbit(int x){return x&(-x);}
ll read(){ll x;scanf("%lld",&x);return x;}
ll c[N],b[N],a[N];
int n,l[N],r[N],tot,m,dfn[N];
vector<int>e[N];
void add(int x,int v)
{
    while(x<=n)
    {
        c[x]+=v;
        x+=lowbit(x);
    }
}
ll ask(int x)
{
    ll t=0;
    while(x)
    {
        t+=c[x];
        x-=lowbit(x);
    }
    return t;
}
void dfs(int x)
{
    dfn[x]=++tot;
    b[tot]=a[x];
    l[x]=tot;
    for(auto y:e[x])
    {
        dfs(y);
    }
    r[x]=tot;
}
int main()
{
    freopen("place.in","r",stdin);
    freopen("place.out","w",stdout);
    n=read();m=read();
    a[1]=read();
    for(int i=2;i<=n;i++)
    {
        a[i]=read();
        e[read()].push_back(i);
    }
    dfs(1);
    for(int i=n;i>=1;i--)
    {
        b[i]=b[i]-b[i-1];
        add(i,b[i]);
    }
    for(;m;m--)
    {
        char c;
        cin>>c;
        if(c=='p')
        {
            int x=read(),v=read();
            add(l[x]+1,v);
            add(r[x]+1,-v);
        }
        else
        {
            int x=read();
            cout<<ask(dfn[x])<<"\n";
        }
    }
}
工资查询

为了解决树上链的修改、询问,我们引入重儿子的定义:x的所有儿子中siz最大的为x的重儿子,其他的称为轻儿子。x和自己的重儿子连边称为重边,重边们组成重链;轻儿子会再开一个重链,和x的连边称为轻边。

这需要两次dfs,第一次计算siz,第二次确定重儿子son,每个点所在重链的起始节点bel,dfs序dfn。特别是dfn需要在第二次先走重儿子,再走轻儿子时计算,不能第一次随意走,计算siz时就确定dfn。

树链剖分后,同一重链的节点们的dfs序是连续的,同时也满足同一子树的节点们dfs序连续,结合线段树,主席树等数据结构可以处理链的修改和子树修改了。

jzyz576 LCA模板

树链剖分后也可以用于求lca:比较bel[x]和bel[y]的深度,谁更大就跳谁,直到bel[x]==bel[y],此时比较siz[x]和siz[y]或者dep[x]和dep[y]即可求得lca。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=500010;
int lowbit(int x){return x&(-x);}
ll read(){ll x;scanf("%lld",&x);return x;}
int n,m,dep[N],siz[N],bel[N],fa[N];
vector<int>e[N];
void dfs(int x)
{
    siz[x]=1;
    for(auto y:e[x]){
        if(y==fa[x])
            continue;
        dep[y]=dep[x]+1;
        fa[y]=x;
        dfs(y);
        siz[x]+=siz[y];
    }
}
void dfs(int x,int chain)
{
    bel[x]=chain;
    int k=0;
    for(auto y:e[x])
    {
        if(y==fa[x])
            continue;
        if(siz[y]>siz[k])
            k=y;
    }
    if(k)//如果不是叶子
        dfs(k,chain);
    for(auto y:e[x])
    {
        if(y==fa[x]||y==k)
            continue;
        dfs(y,y);
    }
}
int lca(int x,int y)
{
    while(bel[x]!=bel[y])
    {
        if(dep[bel[x]]>dep[bel[y]])
            x=fa[bel[x]];
        else
            y=fa[bel[y]];
    }
    // if(bel[x]==bel[y])
    if(siz[x]>siz[y])
        return x;
    return y;



}
int main()
{
    n=read();m=read();
    for(int i=2;i<=n;i++)
    {
        int x=read(),y=read();
        e[x].push_back(y);
        e[y].push_back(x);
    }
    dfs(1);
    dfs(1,1);
    for(;m;m--)
        cout<<lca(read(),read())<<'\n';
}
树链剖分求lca

jzyz luoguP5903 树上 K 级祖先

求k级祖先,由于重链上dfs序连续,因此可以用dep[x]-dep[bel[x]]<k检查重链起始位置距离x是否大于等于k。如果小于,应该跳到重链起始位置再往上跳一下:x=fa[bel[x]],跳轻链跳到下一重链上,直到k级祖先和x在同一个重链上。

在同一个重链上了之后,k级祖先的dfs序也就有了,是dfn[x]-k。有了dfs序,从dfs序反向可以得到k级祖先。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=500010;
int lowbit(int x){return x&(-x);}
ll read(){ll x;scanf("%lld",&x);return x;}
int n,dep[N],siz[N],bel[N],fa[N],dfn[N],nfd[N],tot;
vector<int>e[N];
void dfs(int x)
{
    siz[x]=1;
    for(auto y:e[x]){
        dep[y]=dep[x]+1;
        fa[y]=x;
        dfs(y);
        siz[x]+=siz[y];
    }
}
void dfs(int x,int chain)
{
    bel[x]=chain;
    dfn[x]=++tot;
    nfd[tot]=x;
    int k=0;
    for(auto y:e[x])
    {
        if(siz[y]>siz[k])
            k=y;
    }
    if(k)//如果不是叶子
        dfs(k,chain);
    for(auto y:e[x])
    {
        if(y==k)
            continue;
        dfs(y,y);
    }
}
int getx(int x,int k)
{    
    while(x!=0&&dep[x]-dep[bel[x]]<k)
    {
        k-=dep[x]-dep[bel[x]]+1;
        x=fa[bel[x]];
    }    
    if(x==0)
        return 0;
    int t=dfn[x]-k;
    return nfd[t];
}
int ans[5000010];
unsigned int s;
unsigned int get(unsigned int x)
{
    x^=x<<13;
    x^=x>>17;
    x^=x<<5;
    return s=x;
}
int main()
{
    n=read();int q=read();s=read();
    int rt;
    for(int i=1;i<=n;i++)
    {
        int f=read();
        if(f==0)
            rt=i;
        else
            e[f].push_back(i);
    }
    dep[rt]=1;
    dfs(rt);
    dfs(rt,rt);
    ll sum=0;
    for(ll i=1;i<=q;i++)
    {
        int x=(get(s)^ans[i-1])%n+1;
        int k=(get(s)^ans[i-1])%dep[x];
        ans[i]=getx(x,k);
        sum=sum^(i*ans[i]);
    }
    cout<<sum;
}
树上K级祖先

jzyz1525 严格次小生成树

先构造一个最小生成树

枚举没选上的边x,y,w,看最小生成树上的x到y的路径上的边权们,如果最大值=w,我们应该用x,y,w替换次大值。否则应该替换掉最大值。

因此需要m-n+1次询问路径最大值,严格次大值,使用线段树即可。注意严格次大值。

蓝书上使用了倍增lca来解决,推荐大家写一下树链剖分版本。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100010;
int lowbit(int x){return x&(-x);}
ll read(){ll x;scanf("%lld",&x);return x;}
int n,m,dep[N],siz[N],bel[N],fa[N],dfn[N],tot,a[N];
vector<pair<int,int>>e[N];
pair<int,int>c[N*4];
int get(int x)
{
    return fa[x]==x?x:fa[x]=get(fa[x]);
}
void dfs(int x)
{
    siz[x]=1;
    for(auto [y,w]:e[x]){
        if(y==fa[x])
            continue;
        dep[y]=dep[x]+1;
        fa[y]=x;
        dfs(y);
        siz[x]+=siz[y];
    }
}
void dfs(int x,int chain)
{
    dfn[x]=++tot;bel[x]=chain;
    int k=0;
    for(auto [y,w]:e[x])
    {
        if(y==fa[x])continue;
        if(siz[y]>siz[k])
            k=y;
    }
    if(k)//如果不是叶子
        dfs(k,chain);
    for(auto [y,w]:e[x])
    {
        if(y==fa[x]||y==k)continue;
        dfs(y,y);
    }
}
pair<int,int>merge(pair<int,int>a,pair<int,int>b)
{
    int cc[]={a.first,a.second,b.first,b.second};
    sort(cc,cc+4);
    for(int i=2;i>=0;i--)
        if(cc[i]!=cc[3])
            return {cc[3],cc[i]};
}
pair<int,int> ask(int x,int l,int r,int tl,int tr)
{
    if(tl<=l&&r<=tr)
        return c[x];
    pair<int,int>t={-1e9,-1e9};
    int mid=(l+r)/2;
    if(tl<=mid)
        t=merge(t,ask(x*2,l,mid,tl,tr));
    if(tr>mid)
        t=merge(t,ask(x*2+1,mid+1,r,tl,tr));
    return t;
}
pair<int,int> askmaxx(int x,int y)
{
    pair<int,int>maxx={-1e9,-1e9};
    while(bel[x]!=bel[y])
    {
        if(dep[bel[x]]>dep[bel[y]])
        {
            maxx=merge(maxx,ask(1,1,n,dfn[bel[x]],dfn[x]));
            x=fa[bel[x]];
        }
        else
        {
            maxx=merge(maxx,ask(1,1,n,dfn[bel[y]],dfn[y]));
            y=fa[bel[y]];
        }
    }

    if(siz[x]>siz[y])
        maxx=merge(maxx,ask(1,1,n,dfn[x]+1,dfn[y]));
    else if(siz[x]<siz[y])
        maxx=merge(maxx,ask(1,1,n,dfn[y]+1,dfn[x]));
    return maxx;
}
void add(int x,int l,int r,int d,ll v)
{
    if(l==r)
    {
        c[x]={v,-1e9};
        return ;
    }
    int mid=(l+r)/2;
    if(d<=mid)
        add(x*2,l,mid,d,v);
    else
        add(x*2+1,mid+1,r,d,v);
    c[x]=max(c[x*2],c[x*2+1]);
}
struct edge
{
    int x,y,w,flag;
    friend bool operator <(edge a,edge b)
    {
        return a.w<b.w;
    }
}ee[300010];
ll sum;
void kru()
{
    sort(ee+1,ee+1+m);
    for(int i=1;i<=n;i++)
        fa[i]=i;
    for(int i=1;i<=m;i++)
    {
        if(get(ee[i].x)==get(ee[i].y))
            continue;
        ee[i].flag=1;//用上了这条边
        sum+=ee[i].w;
        e[ee[i].x].push_back({ee[i].y,ee[i].w});
        e[ee[i].y].push_back({ee[i].x,ee[i].w});
        fa[get(ee[i].x)]=get(ee[i].y);
        
    }
    memset(fa,0,sizeof(fa));
}
int main()
{
    freopen("test.in","r",stdin);
    freopen("test.out","w",stdout);
    n=read();m=read();
    for(int i=1;i<=m;i++)
        ee[i]={read(),read(),read()};
    kru();
    dfs(1);
    dfs(1,1);
    for(int i=2;i<=n;i++)
        for(auto [y,w]:e[i])
            if(y==fa[i])
                add(1,1,n,dfn[i],w);//i到父亲的边
    ll ans=1e18;
    for(int i=1;i<=m;i++)
    {
        if(ee[i].flag)
            continue;
        pair<int,int>t=askmaxx(ee[i].x,ee[i].y);
        if(t.first!=ee[i].w)
            ans=min(ans,sum+ee[i].w-t.first);
        else 
            ans=min(ans,sum+ee[i].w-t.second);
    }
    cout<<ans;
}
严格次小生成树

CF733F Drivers Dissatisfaction

首先一个结论是一定对着一个边狠狠地花钱,没有必要在一条以上的边上花钱。

这样一条边花完钱后变成了w-S/c,非常地好做

跑完最小生成树跑树链剖分,最后枚举每条边,如果他在最小生成树里,最终收益是sum-S/c。如果不在最小生成树里,他可以替代最小生成树上x到y的最大边,最终收益是sum-maxx+w-S/c。

最后输出方案,是否需要维护最大值的同时维护最大值的边的编号呢?其实没有必要,记录一下当前最小值是修改了pos号边得来的,最后修改一下pos号边再跑一次克鲁斯卡尔输出答案即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200010;
int lowbit(int x){return x&(-x);}
ll read(){ll x;scanf("%lld",&x);return x;}
int n,m,dep[N],siz[N],bel[N],fa[N],dfn[N],tot,a[N];
vector<pair<int,int>>e[N];
int maxx[N*4];
int get(int x)
{
    return fa[x]==x?x:fa[x]=get(fa[x]);
}
void dfs(int x)
{
    siz[x]=1;
    for(auto [y,w]:e[x]){
        if(y==fa[x])
            continue;
        dep[y]=dep[x]+1;
        fa[y]=x;
        dfs(y);
        siz[x]+=siz[y];
    }
}
void dfs(int x,int chain)
{
    dfn[x]=++tot;bel[x]=chain;
    int k=0;
    for(auto [y,w]:e[x])
    {
        if(y==fa[x])continue;
        if(siz[y]>siz[k])
            k=y;
    }
    if(k)//如果不是叶子
        dfs(k,chain);
    for(auto [y,w]:e[x])
    {
        if(y==fa[x]||y==k)continue;
        dfs(y,y);
    }
}
int ask(int x,int l,int r,int tl,int tr)
{
    if(tl<=l&&r<=tr)
        return maxx[x];
    int t=-1e9;
    int mid=(l+r)/2;
    if(tl<=mid)
        t=max(t,ask(x*2,l,mid,tl,tr));
    if(tr>mid)
        t=max(t,ask(x*2+1,mid+1,r,tl,tr));
    return t;
}
int askmaxx(int x,int y)
{
    int maxx=-1e9;
    while(bel[x]!=bel[y])
    {
        if(dep[bel[x]]>dep[bel[y]])
        {
            maxx=max(maxx,ask(1,1,n,dfn[bel[x]],dfn[x]));
            x=fa[bel[x]];
        }
        else
        {
            maxx=max(maxx,ask(1,1,n,dfn[bel[y]],dfn[y]));
            y=fa[bel[y]];
        }
    }

    if(siz[x]>siz[y])
        maxx=max(maxx,ask(1,1,n,dfn[x]+1,dfn[y]));
    else if(siz[x]<siz[y])
        maxx=max(maxx,ask(1,1,n,dfn[y]+1,dfn[x]));
    return maxx;
}
void add(int x,int l,int r,int d,int v)
{
    if(l==r)
    {
        maxx[x]=v;
        return ;
    }
    int mid=(l+r)/2;
    if(d<=mid)
        add(x*2,l,mid,d,v);
    else
        add(x*2+1,mid+1,r,d,v);
    maxx[x]=max(maxx[x*2],maxx[x*2+1]);
}
struct edge
{
    int x,y,w,c,flag,i;
    friend bool operator <(edge a,edge b)
    {
        return a.w<b.w;
    }
}ee[300010];
ll sum;
void kru()
{
    sort(ee+1,ee+1+m);
    for(int i=1;i<=n;i++)
        fa[i]=i;
    for(int i=1;i<=m;i++)
    {
        if(get(ee[i].x)==get(ee[i].y))
            continue;
        ee[i].flag=1;//用上了这条边
        sum+=ee[i].w;
        e[ee[i].x].push_back({ee[i].y,ee[i].w});
        e[ee[i].y].push_back({ee[i].x,ee[i].w});
        fa[get(ee[i].x)]=get(ee[i].y);
        
    }
    memset(fa,0,sizeof(fa));
}
int main()
{
    n=read();m=read();
    for(int i=1;i<=m;i++)
    {
        ee[i].w=read();
        ee[i].i=i;
    }
    for(int i=1;i<=m;i++)
        ee[i].c=read();
    for(int i=1;i<=m;i++)
    {
        ee[i].x=read();
        ee[i].y=read();
    }
    kru();
    dfs(1);
    dfs(1,1);
    for(int i=2;i<=n;i++)
        for(auto [y,w]:e[i])
            if(y==fa[i])
                add(1,1,n,dfn[i],w);//i到父亲的边
    int S=read();
    ll ans=1e18,pos=0;
    for(int i=1;i<=m;i++)
    {
        ll v;
        if(ee[i].flag)
        {
            v=sum-S/ee[i].c;
            if(v<ans)
                ans=v,pos=i;
        }
        else
        {
            v=sum-askmaxx(ee[i].x,ee[i].y)+ee[i].w-S/ee[i].c;
            if(v<ans)
                ans=v,pos=i;
        }
        ee[i].flag=0;
    }
    ee[pos].w-=S/ee[pos].c;
    kru();
    cout<<ans<<'\n';
    for(int i=1;i<=m;i++)
        if(ee[i].flag)
            cout<<ee[i].i<<' '<<ee[i].w<<'\n';
}
Drivers Dissatisfaction

BZOJ2238 Mst

先不管无解了,看看有解的时候怎么办

和上一题类似,爬完最小生成树跑树链剖分

注意到如果被删的边是非树边,答案依然是sum。否则答案是sum-ee[i].w+所有覆盖(ee[i].x,ee[i].y)这条边的非树边边权的最小值

因此拿着非树边更新树上路径的最小值即可。

如果最小生成树没跑出来,说明并非联通,直接q次循环每次输出Not connected即可

否则,看看这条边的最小值,如果是0x3f3f3f3f说明没有被更新过,意思是没有非树边覆盖这条边,删了这条边就不了联通了,因此输出Not connected

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100010;
int lowbit(int x){return x&(-x);}
ll read(){ll x;scanf("%lld",&x);return x;}
int n,m,dep[N],siz[N],bel[N],fa[N],dfn[N],tot,a[N];
vector<pair<int,int>>e[N];
int c[N*4];
int get(int x)
{
    return fa[x]==x?x:fa[x]=get(fa[x]);
}
void dfs(int x)
{
    siz[x]=1;
    for(auto [y,w]:e[x]){
        if(y==fa[x])
            continue;
        dep[y]=dep[x]+1;
        fa[y]=x;
        dfs(y);
        siz[x]+=siz[y];
    }
}
void dfs(int x,int chain)
{
    dfn[x]=++tot;bel[x]=chain;
    int k=0;
    for(auto [y,w]:e[x])
    {
        if(y==fa[x])continue;
        if(siz[y]>siz[k])
            k=y;
    }
    if(k)//如果不是叶子
        dfs(k,chain);
    for(auto [y,w]:e[x])
    {
        if(y==fa[x]||y==k)continue;
        dfs(y,y);
    }
}
int ask(int x,int l,int r,int d)
{
    if(l==r)
        return c[x];
    int mid=(l+r)/2;
    if(d<=mid)
        return min(c[x],ask(x*2,l,mid,d));
    else
        return min(c[x],ask(x*2+1,mid+1,r,d));
}
void add(int x,int l,int r,int tl,int tr,int v)
{
    if(tl<=l&&r<=tr)
    {
        c[x]=min(c[x],v);
        return ;
    }
    int mid=(l+r)/2;
    if(tl<=mid)
        add(x*2,l,mid,tl,tr,v);
    if(tr>mid)
        add(x*2+1,mid+1,r,tl,tr,v);
}

void lca_add(int x,int y,int v)//询问最小值
{
    while(bel[x]!=bel[y])
    {
        if(dep[bel[x]]>dep[bel[y]])
        {
            add(1,1,n,dfn[bel[x]],dfn[x],v);
            x=fa[bel[x]];
        }
        else
        {
            add(1,1,n,dfn[bel[y]],dfn[y],v);
            y=fa[bel[y]];
        }
    }
    if(siz[x]>siz[y])
        add(1,1,n,dfn[x]+1,dfn[y],v);
    else if(siz[x]<siz[y])
        add(1,1,n,dfn[y]+1,dfn[x],v);
}
struct edge
{
    int x,y,w,i,flag;
    friend bool operator <(edge a,edge b)
    {
        return a.w<b.w;
    }
}ee[300010];
ll sum;
void kru()
{
    sort(ee+1,ee+1+m);
    for(int i=1;i<=n;i++)
        fa[i]=i;
    int cntt=0;
    for(int i=1;i<=m;i++)
    {
        if(get(ee[i].x)==get(ee[i].y))
            continue;
        cntt++;
        ee[i].flag=1;//用上了这条边
        sum+=ee[i].w;
        e[ee[i].x].push_back({ee[i].y,ee[i].w});
        e[ee[i].y].push_back({ee[i].x,ee[i].w});
        fa[get(ee[i].x)]=get(ee[i].y);
    }
    memset(fa,0,sizeof(fa));
    if(cntt!=n-1)
    {
        for(int q=read();q;q--)
            cout<<"Not connected\n";
        exit(0);
    }
}
bool i_(edge a,edge b)
{
    return a.i<b.i;
}
int main()
{
    // freopen("test.in","r",stdin);
    // freopen("test.out","w",stdout);
    n=read();m=read();
    for(int i=1;i<=m;i++)
        ee[i]={read(),read(),read(),i};
    kru();
    dfs(1);
    dfs(1,1);
    memset(c,0x3f,sizeof(c));
    sort(ee+1,ee+1+m,i_);
    for(int i=1;i<=m;i++)
    {
        if(ee[i].flag)
            continue;//被用了,我的答案一会再求
        //没被用,答案是sum,可以更新别人的答案
        lca_add(ee[i].x,ee[i].y,ee[i].w);
    }
    for(int q=read();q;q--)
    {
        int t=read();
        if(ee[t].flag)
        {
            int x=ee[t].x,y=ee[t].y;
            if(siz[x]>siz[y])
                swap(x,y);
            if(ask(1,1,n,dfn[x])==0x3f3f3f3f)
                cout<<"Not connected\n";
            else
                cout<<sum-ee[t].w+ask(1,1,n,dfn[x])<<'\n';
        }
        else
            cout<<sum<<'\n';
    }
}
BZOJ2238 Mst

jzyz593 树分治例3 spoj375 qtree

在这里讲一下树链剖分怎么处理边的事情:

我的做法是把xyw放在sizx和sizy较小的地方,谁小放在谁那里,也就是儿子那边。这样修改的时候对着线段树上dfn[x]修改即可。询问的时候跳x/跳y依然是ask(1,1,n,dfn[bel[x]],dfn[x])/ask(1,1,n,dfn[bel[y]],dfn[y])。只有最后在同一个重链的时候,比如x在y的上面,你需要对区间[dfn[x]+1,dfn[y]]取max,不能算上dfn[x],因为这个对应的边是lca到fa[lca]的。如果最后x=y了,则应该不取max。

这题多组数据,数组初始化这块需要注意

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=10010;
int lowbit(int x){return x&(-x);}
ll read(){ll x;scanf("%lld",&x);return x;}
int n,dep[N],siz[N],bel[N],fa[N],dfn[N],tot;
vector<pair<int,int>>e[N];
struct node
{
    int x,y,w,i;
}ee[N];
int maxx[N*4];
void add(int x,int l,int r,int d,int v)
{
    if(l==r){
        maxx[x]=v;
        return ;
    }
    int mid=(l+r)/2;
    if(d<=mid)
        add(x*2,l,mid,d,v);
    else
        add(x*2+1,mid+1,r,d,v);
    maxx[x]=max(maxx[x*2],maxx[x*2+1]);
}
int ask(int x,int l,int r,int tl,int tr)
{
    if(tl<=l&&r<=tr)
        return maxx[x];
    int mid=(l+r)/2,t=0;
    if(tl<=mid)
        t=ask(x*2,l,mid,tl,tr);
    if(tr>mid)
        t=max(t,ask(x*2+1,mid+1,r,tl,tr));
    return t;
}
void dfs(int x)
{
    siz[x]=1;
    for(auto [y,w]:e[x]){
        if(y==fa[x])
            continue;
        dep[y]=dep[x]+1;
        fa[y]=x;
        dfs(y);
        siz[x]+=siz[y];
    }
}
void dfs(int x,int chain)
{
    bel[x]=chain;
    dfn[x]=++tot;
    int k=0;
    for(auto [y,w]:e[x])
    {
        if(fa[x]==y){
            add(1,1,n,dfn[x],w);//单点修改
            continue;
        }
        if(siz[y]>siz[k])
            k=y;
    }
    if(k)//如果不是叶子
        dfs(k,chain);
    for(auto [y,w]:e[x])
    {
        if(y==k||y==fa[x])
            continue;
        dfs(y,y);
    }
}
int lca_max(int x,int y)
{
    int t=0;
    while(bel[x]!=bel[y])
    {
        if(dep[bel[x]]>dep[bel[y]])
        {
            t=max(t,ask(1,1,n,dfn[bel[x]],dfn[x]));
            x=fa[bel[x]];
        }
        else
        {
            t=max(t,ask(1,1,n,dfn[bel[y]],dfn[y]));
            y=fa[bel[y]];
        }
    }
    if(siz[x]>siz[y])
        t=max(t,ask(1,1,n,dfn[x]+1,dfn[y]));
    else if(siz[x]<siz[y])
        t=max(t,ask(1,1,n,dfn[y]+1,dfn[x]));
    return t;
}
void work()
{
    n=read();
    for(int i=1;i<=n;i++)
        e[i].clear();
    tot=0;
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read(),w=read();
        ee[i]={x,y,w,i};
        e[x].push_back({y,w});
        e[y].push_back({x,w});
        dep[i]=0;
    }
    memset(maxx,0,n*sizeof(int));
    dep[1]=read();
    dfs(1);
    dfs(1,1);
    for(int i=1;i<n;i++)
    {
        if(dep[ee[i].x]<dep[ee[i].y])
            swap(ee[i].x,ee[i].y);
    }
    while(1)
    {
        string s;
        cin>>s;
        if(s=="DONE")
            return ;
        if(s=="CHANGE")
        {
            int i=read(),w=read();
            int x=ee[i].x;
            add(1,1,n,dfn[x],w);
        }
        else
            cout<<lca_max(read(),read())<<'\n';
    }
}
int main()
{
    // freopen("QTREE1.in","r",stdin);
    // freopen("QTREE1.out","w",stdout);
    for(int t=read();t;t--)
        work();
}
spoj375 qtree

BZOJ1984. 月下“毛景树”

还是边的事情,继续像上几题这样处理

区间覆盖,区间加,区间询问max,我们同时维护区间覆盖懒标记lz,区间加懒标记ad,区间最大值maxx

遇到修改这样处理即可:

 

 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100010;
int lowbit(int x){return x&(-x);}
ll read(){ll x;scanf("%lld",&x);return x;}
int n,dep[N],siz[N],bel[N],fa[N],dfn[N],tot;
vector<pair<int,int>>e[N];
struct node
{
    int x,y,w;
}ee[N];
int maxx[N*4],lz[N*4],ad[N*4];
void pushdown(int x)
{
    if(lz[x]==-1)//没有区间覆盖
    {
        if(ad[x]==0)//不需要push
            return ;
        if(lz[x*2]==-1)
            ad[x*2]+=ad[x];
        else
            lz[x*2]+=ad[x];
        if(lz[x*2+1]==-1)
            ad[x*2+1]+=ad[x];
        else
            lz[x*2+1]+=ad[x];
        maxx[x*2]+=ad[x];
        maxx[x*2+1]+=ad[x];
    }
    else
    {
        lz[x*2]=lz[x*2+1]=lz[x];
        ad[x*2]=ad[x*2+1]=0;
        maxx[x*2]=maxx[x*2+1]=lz[x];
    }
    lz[x]=-1;
    ad[x]=0;
}
void add(int x,int l,int r,int tl,int tr,int v)//区间加
{
    if(tl<=l&&r<=tr) 
    {
        if(lz[x]==-1)
            ad[x]+=v;
        else
            lz[x]+=v;
        maxx[x]+=v;
        return ;
    }
    pushdown(x);
    int mid=(l+r)/2;
    if(tl<=mid)
        add(x*2,l,mid,tl,tr,v);
    if(tr>mid)
        add(x*2+1,mid+1,r,tl,tr,v);
    maxx[x]=max(maxx[x*2],maxx[x*2+1]);
}
void cover(int x,int l,int r,int tl,int tr,int w)//区间覆盖
{
    if(tl<=l&&r<=tr)
    {
        maxx[x]=lz[x]=w;
        ad[x]=0;
        return ;
    }
    pushdown(x);
    int mid=(l+r)/2;
    if(tl<=mid)
        cover(x*2,l,mid,tl,tr,w);
    if(tr>mid)
        cover(x*2+1,mid+1,r,tl,tr,w);
    maxx[x]=max(maxx[x*2],maxx[x*2+1]);
}
int ask(int x,int l,int r,int tl,int tr)//区间询问最大值
{
    if(tl<=l&&r<=tr)
        return maxx[x];
    pushdown(x);
    int mid=(l+r)/2,t=0;
    if(tl<=mid)
        t=ask(x*2,l,mid,tl,tr);
    if(tr>mid)
        t=max(t,ask(x*2+1,mid+1,r,tl,tr));
    return t;
}
void dfs(int x)
{
    siz[x]=1;
    for(auto [y,w]:e[x]){
        if(y==fa[x])
            continue;
        dep[y]=dep[x]+1;
        fa[y]=x;
        dfs(y);
        siz[x]+=siz[y];
    }
}
void dfs(int x,int chain)
{
    bel[x]=chain;
    dfn[x]=++tot;
    int k=0;
    for(auto [y,w]:e[x])
    {
        if(fa[x]==y){
            add(1,1,n,dfn[x],dfn[x],w);//单点修改
            continue;
        }
        if(siz[y]>siz[k])
            k=y;
    }
    if(k)//如果不是叶子
        dfs(k,chain);
    for(auto [y,w]:e[x])
    {
        if(y==k||y==fa[x])
            continue;
        dfs(y,y);
    }
}

void lca_cover(int x,int y,int w)
{
    while(bel[x]!=bel[y])
    {
        if(dep[bel[x]]>dep[bel[y]])
        {
            cover(1,1,n,dfn[bel[x]],dfn[x],w);
            x=fa[bel[x]];
        }
        else
        {
            cover(1,1,n,dfn[bel[y]],dfn[y],w);
            y=fa[bel[y]];
        }
    }
    if(siz[x]>siz[y])
        cover(1,1,n,dfn[x]+1,dfn[y],w);
    else if(siz[x]<siz[y])
        cover(1,1,n,dfn[y]+1,dfn[x],w);
}
void lca_add(int x,int y,int w)
{
    while(bel[x]!=bel[y])
    {
        if(dep[bel[x]]>dep[bel[y]])
        {
            add(1,1,n,dfn[bel[x]],dfn[x],w);
            x=fa[bel[x]];
        }
        else
        {
            add(1,1,n,dfn[bel[y]],dfn[y],w);
            y=fa[bel[y]];
        }
    }
    if(siz[x]>siz[y])
        add(1,1,n,dfn[x]+1,dfn[y],w);
    else if(siz[x]<siz[y])
        add(1,1,n,dfn[y]+1,dfn[x],w);
}
int lca_max(int x,int y)
{
    int t=0;
    while(bel[x]!=bel[y])
    {
        if(dep[bel[x]]>dep[bel[y]])
        {
            t=max(t,ask(1,1,n,dfn[bel[x]],dfn[x]));
            x=fa[bel[x]];
        }
        else
        {
            t=max(t,ask(1,1,n,dfn[bel[y]],dfn[y]));
            y=fa[bel[y]];
        }
    }
    if(siz[x]>siz[y])
        t=max(t,ask(1,1,n,dfn[x]+1,dfn[y]));
    else if(siz[x]<siz[y])
        t=max(t,ask(1,1,n,dfn[y]+1,dfn[x]));
    return t;
}
int main()
{
    memset(lz,-1,sizeof(lz));
    n=read();
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read(),w=read();
        ee[i]={x,y,w};
        e[x].push_back({y,w});
        e[y].push_back({x,w});
    }
    dep[1]=1;
    dfs(1);
    dfs(1,1);
    for(int i=1;i<n;i++)
        if(dep[ee[i].x]<dep[ee[i].y])
            swap(ee[i].x,ee[i].y);
    while(1)
    {
        string s;
        cin>>s;
        if(s=="Stop")
            break ;
        if(s=="Change")
        {
            int k=read(),w=read();
            cover(1,1,n,dfn[ee[k].x],dfn[ee[k].x],w);
        }
        else if(s=="Cover")
        {
            int x=read(),y=read(),w=read();
            lca_cover(x,y,w);
        }
        else if(s=="Add")
        {
            int x=read(),y=read(),w=read();
            lca_add(x,y,w);
        }
        else
            cout<<lca_max(read(),read())<<'\n';
    }
}
月下毛景树

 BZOJ2157 [国家集训队] 旅游

是大杂烩,写起来不是很难

依然是路径的问题,单点修改,区间取反,区间求和,区间最大值,区间最小值

取反的时候,区间和当然是加负号。maxx和minn我是这样做的:

minn[x]=-minn[x];
maxx[x]=-maxx[x];
swap(minn[x],maxx[x]);

感觉很对!

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200010;
int lowbit(int x){return x&(-x);}
ll read(){ll x;scanf("%lld",&x);return x;}
int n,dep[N],siz[N],bel[N],fa[N],dfn[N],tot;
vector<pair<int,int>>e[N];
struct node
{
    int x,y,w;
}ee[N];
int maxx[N*4],minn[N*4],lz[N*4],sum[N*4];
void NOT(int x)//线段树节点x取反
{
    lz[x]^=1;
    sum[x]=-sum[x];
    minn[x]=-minn[x];
    maxx[x]=-maxx[x];
    swap(minn[x],maxx[x]);
}
void pushdown(int x)
{
    if(lz[x])
    {
        NOT(x*2);
        NOT(x*2+1);
        lz[x]=0;
    }
}
void add(int x,int l,int r,int d,int v)//单点修改
{
    if(l==r) 
    {
        minn[x]=maxx[x]=sum[x]=v;
        return ;
    }
    pushdown(x);
    int mid=(l+r)/2;
    if(d<=mid)
        add(x*2,l,mid,d,v);
    else
        add(x*2+1,mid+1,r,d,v);
    maxx[x]=max(maxx[x*2],maxx[x*2+1]);
    minn[x]=min(minn[x*2],minn[x*2+1]);
    sum[x]=sum[x*2]+sum[x*2+1];
}
void NOT(int x,int l,int r,int tl,int tr)//区间取反
{
    if(tl<=l&&r<=tr)
    {
        NOT(x);
        return ;
    }
    pushdown(x);
    int mid=(l+r)/2;
    if(tl<=mid)
        NOT(x*2,l,mid,tl,tr);
    if(tr>mid)
        NOT(x*2+1,mid+1,r,tl,tr);
    maxx[x]=max(maxx[x*2],maxx[x*2+1]);
    minn[x]=min(minn[x*2],minn[x*2+1]);
    sum[x]=sum[x*2]+sum[x*2+1];
}
int asksum(int x,int l,int r,int tl,int tr)//区间询问最大值
{
    if(tl<=l&&r<=tr)
        return sum[x];
    pushdown(x);
    int mid=(l+r)/2,t=0;
    if(tl<=mid)
        t+=asksum(x*2,l,mid,tl,tr);
    if(tr>mid)
        t+=asksum(x*2+1,mid+1,r,tl,tr);
    return t;
}

int askmin(int x,int l,int r,int tl,int tr)
{
    if(tl<=l&&r<=tr)
        return minn[x];
    pushdown(x);
    int mid=(l+r)/2,t=1e9;
    if(tl<=mid)
        t=askmin(x*2,l,mid,tl,tr);
    if(tr>mid)
        t=min(t,askmin(x*2+1,mid+1,r,tl,tr));
    return t;
}
int askmax(int x,int l,int r,int tl,int tr)
{
    if(tl<=l&&r<=tr)
        return maxx[x];
    pushdown(x);
    int mid=(l+r)/2,t=-1e9;
    if(tl<=mid)
        t=askmax(x*2,l,mid,tl,tr);
    if(tr>mid)
        t=max(t,askmax(x*2+1,mid+1,r,tl,tr));
    return t;
}
void dfs(int x)
{
    siz[x]=1;
    for(auto [y,w]:e[x]){
        if(y==fa[x])
            continue;
        dep[y]=dep[x]+1;
        fa[y]=x;
        dfs(y);
        siz[x]+=siz[y];
    }
}
void dfs(int x,int chain)
{
    bel[x]=chain;
    dfn[x]=++tot;
    int k=0;
    for(auto [y,w]:e[x])
    {
        if(fa[x]==y){
            add(1,1,n,dfn[x],w);//单点修改
            continue;
        }
        if(siz[y]>siz[k])
            k=y;
    }
    if(k)//如果不是叶子
        dfs(k,chain);
    for(auto [y,w]:e[x])
    {
        if(y==k||y==fa[x])
            continue;
        dfs(y,y);
    }
}

void lca_not(int x,int y)
{
    while(bel[x]!=bel[y])
    {
        if(dep[bel[x]]>dep[bel[y]])
        {
            NOT(1,1,n,dfn[bel[x]],dfn[x]);
            x=fa[bel[x]];
        }
        else
        {
            NOT(1,1,n,dfn[bel[y]],dfn[y]);
            y=fa[bel[y]];
        }
    }
    if(siz[x]>siz[y])
        NOT(1,1,n,dfn[x]+1,dfn[y]);
    else if(siz[x]<siz[y])
        NOT(1,1,n,dfn[y]+1,dfn[x]);
}
int lca_sum(int x,int y)
{
    int t=0;
    while(bel[x]!=bel[y])
    {
        if(dep[bel[x]]>dep[bel[y]])
        {
            t+=asksum(1,1,n,dfn[bel[x]],dfn[x]);
            x=fa[bel[x]];
        }
        else
        {
            t+=asksum(1,1,n,dfn[bel[y]],dfn[y]);
            y=fa[bel[y]];
        }
    }
    if(siz[x]>siz[y])
        t+=asksum(1,1,n,dfn[x]+1,dfn[y]);
    else if(siz[x]<siz[y])
        t+=asksum(1,1,n,dfn[y]+1,dfn[x]);
    return t;
}
int lca_min(int x,int y)
{
    int t=1e9;
    while(bel[x]!=bel[y])
    {
        if(dep[bel[x]]>dep[bel[y]])
        {
            t=min(t,askmin(1,1,n,dfn[bel[x]],dfn[x]));
            x=fa[bel[x]];
        }
        else
        {
            t=min(t,askmin(1,1,n,dfn[bel[y]],dfn[y]));
            y=fa[bel[y]];
        }
    }
    if(siz[x]>siz[y])
        t=min(t,askmin(1,1,n,dfn[x]+1,dfn[y]));
    else if(siz[x]<siz[y])
        t=min(t,askmin(1,1,n,dfn[y]+1,dfn[x]));
    return t;
}
int lca_max(int x,int y)
{
    int t=-1e9;
    while(bel[x]!=bel[y])
    {
        if(dep[bel[x]]>dep[bel[y]])
        {
            t=max(t,askmax(1,1,n,dfn[bel[x]],dfn[x]));
            x=fa[bel[x]];
        }
        else
        {
            t=max(t,askmax(1,1,n,dfn[bel[y]],dfn[y]));
            y=fa[bel[y]];
        }
    }
    if(siz[x]>siz[y])
        t=max(t,askmax(1,1,n,dfn[x]+1,dfn[y]));
    else if(siz[x]<siz[y])
        t=max(t,askmax(1,1,n,dfn[y]+1,dfn[x]));
    return t;
}
int main()
{
    n=read();
    for(int i=1;i<n;i++)
    {
        int x=read()+1,y=read()+1,w=read();
        ee[i]={x,y,w};
        e[x].push_back({y,w});
        e[y].push_back({x,w});
    }
    dep[1]=1;
    dfs(1);
    dfs(1,1);
    for(int i=1;i<n;i++)
        if(dep[ee[i].x]<dep[ee[i].y])
            swap(ee[i].x,ee[i].y);
        int m=read();
    for(int m=read();m;m--)
    {
        string s;
        cin>>s;
        if(s=="C")
        {
            int k=read(),w=read();
            add(1,1,n,dfn[ee[k].x],w);
        }
        else if(s=="N")
            lca_not(read()+1,read()+1);
        else if(s=="SUM")
            cout<<lca_sum(read()+1,read()+1)<<'\n';
        else if(s=="MAX")
            cout<<lca_max(read()+1,read()+1)<<'\n';
        else if(s=="MIN")
            cout<<lca_min(read()+1,read()+1)<<'\n';
    }
}
BZOJ2157 [国家集训队] 旅游

jzyz585 [zjoi2008] 树的统计

单点修改,询问路径权值最大值,询问路径权值和,线段树即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=500010;
int lowbit(int x){return x&(-x);}
ll read(){ll x;scanf("%lld",&x);return x;}
int n,m,dep[N],siz[N],bel[N],fa[N],dfn[N],tot;
vector<int>e[N];
pair<int,int>c[N];
void dfs(int x)
{
    siz[x]=1;
    for(auto y:e[x]){
        if(y==fa[x])
            continue;
        dep[y]=dep[x]+1;
        fa[y]=x;
        dfs(y);
        siz[x]+=siz[y];
    }
}
void dfs(int x,int chain)
{
    dfn[x]=++tot;
    bel[x]=chain;
    int k=0;
    for(auto y:e[x])
    {
        if(y==fa[x])
            continue;
        if(siz[y]>siz[k])
            k=y;
    }
    if(k)//如果不是叶子
        dfs(k,chain);
    for(auto y:e[x])
    {
        if(y==fa[x]||y==k)
            continue;
        dfs(y,y);
    }
}
pair<int,int>ask(int x,int l,int r,int tl,int tr)
{
    assert(tl<=tr);
    if(tl<=l&&r<=tr)
        return c[x];
    pair<int,int>t={-4e4,0};
    int mid=(l+r)/2;
    if(tl<=mid)
    {
        auto tt=ask(x*2,l,mid,tl,tr);
        t.first=max(tt.first,t.first);
        t.second+=tt.second;
    }
    if(tr>mid)
    {
        auto tt=ask(x*2+1,mid+1,r,tl,tr);
        t.first=max(tt.first,t.first);
        t.second+=tt.second;
    }
    return t;
}
int askmax(int x,int y)
{
    int maxx=-5e4;
    while(bel[x]!=bel[y])
    {
        if(dep[bel[x]]>dep[bel[y]])
        {
            maxx=max(maxx,ask(1,1,n,dfn[bel[x]],dfn[x]).first);
            x=fa[bel[x]];
        }
        else{
            maxx=max(maxx,ask(1,1,n,dfn[bel[y]],dfn[y]).first);
            y=fa[bel[y]];
        }
    }
    if(siz[x]>siz[y])
        maxx=max(maxx,ask(1,1,n,dfn[x],dfn[y]).first);
    else
        maxx=max(maxx,ask(1,1,n,dfn[y],dfn[x]).first);
    return maxx;
}
int asksum(int x,int y)
{
    int summ=0;
    while(bel[x]!=bel[y])
    {
        if(dep[bel[x]]>dep[bel[y]])
        {
            summ+=ask(1,1,n,dfn[bel[x]],dfn[x]).second;
            x=fa[bel[x]];
        }
        else
        {
            summ+=ask(1,1,n,dfn[bel[y]],dfn[y]).second;
            y=fa[bel[y]];
        }
    }
    if(siz[x]>siz[y])
        summ+=ask(1,1,n,dfn[x],dfn[y]).second;
    else
        summ+=ask(1,1,n,dfn[y],dfn[x]).second;
    return summ;
}
void change(int x,int l,int r,int d,int v)
{
    if(l==r)
    {
        c[x]={v,v};
        return ;
    }
    int mid=(l+r)/2;
    if(d<=mid)
        change(x*2,l,mid,d,v);
    else
        change(x*2+1,mid+1,r,d,v);
    c[x].first=max(c[x*2].first,c[x*2+1].first);
    c[x].second=c[x*2].second+c[x*2+1].second;

}
int main()
{
    freopen("data.in","r",stdin);
    freopen("data.out","w",stdout);
    n=read();
    for(int i=2;i<=n;i++)
    {
        int x=read(),y=read();
        e[x].push_back(y);
        e[y].push_back(x);
    }
    dfs(1);
    dfs(1,1);

    for(int i=1;i<=n;i++)
        change(1,1,n,dfn[i],read());
    for(int q=read();q;q--)
    {
        string s;
        cin>>s;
        if(s=="CHANGE")
        {
            int x=read(),t=read();
            change(1,1,n,dfn[x],t);
        }
        else if(s=="QMAX")
        {
            int x=read(),y=read();
            cout<<askmax(x,y)<<'\n';
        }
        else
        {
            int x=read(),y=read();
            cout<<asksum(x,y)<<'\n';
        }
    }
}
树的统计

 jzyz2010 [HAOI2015] 树上操作

单点修改,子树修改,路径询问,继续线段树即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100010;
int lowbit(int x){return x&(-x);}
ll read(){ll x;scanf("%lld",&x);return x;}
int n,m,dep[N],siz[N],bel[N],fa[N],dfn[N],tot,a[N];
vector<int>e[N];
ll sum[N*4],lz[N*4];
void dfs(int x)
{
    siz[x]=1;
    for(auto y:e[x]){
        if(y==fa[x])
            continue;
        dep[y]=dep[x]+1;
        fa[y]=x;
        dfs(y);
        siz[x]+=siz[y];
    }
}
void dfs(int x,int chain)
{
    dfn[x]=++tot;bel[x]=chain;
    int k=0;
    for(auto y:e[x])
    {
        if(y==fa[x])continue;
        if(siz[y]>siz[k])
            k=y;
    }
    if(k)//如果不是叶子
        dfs(k,chain);
    for(auto y:e[x])
    {
        if(y==fa[x]||y==k)continue;
        dfs(y,y);
    }
}
void pushdown(int x,int l,int mid,int r)
{
    if(lz[x])
    {
        lz[x*2]+=lz[x];
        lz[x*2+1]+=lz[x];
        sum[x*2]+=lz[x]*(mid-l+1);
        sum[x*2+1]+=lz[x]*(r-mid);
        lz[x]=0;
    }
}
ll ask(int x,int l,int r,int tl,int tr)
{
    if(tl<=l&&r<=tr)
        return sum[x];
    ll t=0;
    int mid=(l+r)/2;
    pushdown(x,l,mid,r);
    if(tl<=mid)
        t+=ask(x*2,l,mid,tl,tr);
    if(tr>mid)
        t+=ask(x*2+1,mid+1,r,tl,tr);
    return t;
}
ll asksum(int x,int y)
{
    ll summ=0;
    while(bel[x]!=bel[y])
    {
        if(dep[bel[x]]>dep[bel[y]])
        {
            summ+=ask(1,1,n,dfn[bel[x]],dfn[x]);
            x=fa[bel[x]];
        }
        else
        {
            summ+=ask(1,1,n,dfn[bel[y]],dfn[y]);
            y=fa[bel[y]];
        }
    }
    if(siz[x]>siz[y])
        summ+=ask(1,1,n,dfn[x],dfn[y]);
    else
        summ+=ask(1,1,n,dfn[y],dfn[x]);
    return summ;
}
void add(int x,int l,int r,int tl,int tr,ll v)
{
    if(tl<=l&&r<=tr)
    {
        lz[x]+=v;
        sum[x]+=v*(r-l+1);
        return ;
    }
    int mid=(l+r)/2;
    pushdown(x,l,mid,r);
    if(tl<=mid)
        add(x*2,l,mid,tl,tr,v);
    if(tr>mid)
        add(x*2+1,mid+1,r,tl,tr,v);
    sum[x]=sum[x*2]+sum[x*2+1];
}
int main()
{
    freopen("haoi2015.in","r",stdin);
    freopen("haoi2015.out","w",stdout);
    n=read();m=read();
    for(int i=1;i<=n;i++)
        a[i]=read();
    for(int i=2;i<=n;i++)
    {
        int x=read(),y=read();
        e[x].push_back(y);
        e[y].push_back(x);
    }
    dfs(1);
    dfs(1,1);

    for(int i=1;i<=n;i++)
        add(1,1,n,dfn[i],dfn[i],a[i]);
    for(;m;m--)
    {
        int t=read();
        if(t==1)
        {
            int x=read(),v=read();
            add(1,1,n,dfn[x],dfn[x],v);
        }
        else if(t==2)
        {
            int x=read(),v=read();
            add(1,1,n,dfn[x],dfn[x]+siz[x]-1,v);
        }
        else
        {
            int x=read(),y=1;
            cout<<asksum(x,y)<<'\n';
        }
    }
}
树上操作

luogu3384 重链剖分/树链剖分

路径修改,路径询问,子树修改,子树询问,继续线段树即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100010;
int lowbit(int x){return x&(-x);}
ll read(){ll x;scanf("%lld",&x);return x;}
int n,m,dep[N],siz[N],bel[N],fa[N],dfn[N],tot,a[N];
int mod;
vector<int>e[N];
ll c[N*4],lz[N*4];
void dfs(int x)
{
    siz[x]=1;
    for(auto y:e[x]){
        if(y==fa[x])
            continue;
        dep[y]=dep[x]+1;
        fa[y]=x;
        dfs(y);
        siz[x]+=siz[y];
    }
}
void dfs(int x,int chain)
{
    dfn[x]=++tot;bel[x]=chain;
    int k=0;
    for(auto y:e[x])
    {
        if(y==fa[x])continue;
        if(siz[y]>siz[k])
            k=y;
    }
    if(k)//如果不是叶子
        dfs(k,chain);
    for(auto y:e[x])
    {
        if(y==fa[x]||y==k)continue;
        dfs(y,y);
    }
}
void pushdown(int x,int l,int mid,int r)
{
    if(lz[x])
    {
        c[x*2]=(c[x*2]+lz[x]*(mid-l+1))%mod;
        c[x*2+1]=(c[x*2+1]+lz[x]*(r-mid))%mod;
        lz[x*2]=(lz[x*2]+lz[x])%mod;
        lz[x*2+1]=(lz[x*2+1]+lz[x])%mod;
        lz[x]=0;
    }
}
ll ask(int x,int l,int r,int tl,int tr)
{
    if(tl<=l&&r<=tr)
        return c[x];
    ll t=0;
    int mid=(l+r)/2;
    pushdown(x,l,mid,r);
    if(tl<=mid)
        t=(t+ask(x*2,l,mid,tl,tr))%mod;
    if(tr>mid)
        t=(t+ask(x*2+1,mid+1,r,tl,tr))%mod;
    return t;
}
void add(int x,int l,int r,int tl,int tr,ll v)
{
    if(tl<=l&&r<=tr)
    {
        c[x]=(c[x]+v*(r-l+1))%mod;
        lz[x]=(lz[x]+v)%mod;
        return ;
    }
    int mid=(l+r)/2;
    pushdown(x,l,mid,r);
    if(tl<=mid)
        add(x*2,l,mid,tl,tr,v);
    if(tr>mid)
        add(x*2+1,mid+1,r,tl,tr,v);
    c[x]=(c[x*2]+c[x*2+1])%mod;
}
ll lca_sum(int x,int y)
{
    ll summ=0;
    while(bel[x]!=bel[y])
    {
        if(dep[bel[x]]>dep[bel[y]])
        {
            summ=(summ+ask(1,1,n,dfn[bel[x]],dfn[x]))%mod;
            x=fa[bel[x]];
        }
        else
        {
            summ=(summ+ask(1,1,n,dfn[bel[y]],dfn[y]))%mod;
            y=fa[bel[y]];
        }
    }

    if(siz[x]>siz[y])
        summ=(summ+ask(1,1,n,dfn[x],dfn[y]))%mod;
    else
        summ=(summ+ask(1,1,n,dfn[y],dfn[x]))%mod;
    return summ;
}

void lca_add(int x,int y,int v)
{
    while(bel[x]!=bel[y])
    {
        if(dep[bel[x]]>dep[bel[y]])
        {
            add(1,1,n,dfn[bel[x]],dfn[x],v);
            x=fa[bel[x]];
        }
        else
        {
            add(1,1,n,dfn[bel[y]],dfn[y],v);
            y=fa[bel[y]];
        }
    }
    if(siz[x]>siz[y])
        add(1,1,n,dfn[x],dfn[y],v);
    else
        add(1,1,n,dfn[y],dfn[x],v);
}
int main()
{
    n=read();m=read();
    int rt=read();mod=read();
    for(int i=1;i<=n;i++)
        a[i]=read();
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read();
        e[x].push_back(y);
        e[y].push_back(x);
    }
    dep[rt]=1;
    dfs(rt);
    dfs(rt,rt);
    for(int i=1;i<=n;i++)
        add(1,1,n,dfn[i],dfn[i],a[i]);
    for(int i=1;i<=m;i++)
    {
        int t=read();
        if(t==1)
        {
            int x=read(),y=read(),v=read();
            lca_add(x,y,v);
        }
        else if(t==2)
        {
            int x=read(),y=read();
            cout<<lca_sum(x,y)<<'\n';
        }
        else if(t==3)
        {
            int x=read(),v=read();
            add(1,1,n,dfn[x],dfn[x]+siz[x]-1,v);
        }
        else
        {
            int x=read();
            cout<<ask(1,1,n,dfn[x],dfn[x]+siz[x]-1)<<'\n';
        }
    }
}
重链剖分/树链剖分

jzyz586 [树链剖分]模板题

如果不看换根,本题和上一题一样:路径修改,子树修改,路径询问,子树询问

现在有了换根操作,这对于路径修改和路径询问毫无影响

而对于子树操作,如果rt=x,则对整棵树进行修改/询问即可

如果rt并非x的子树内的点,则等价于没换根,依然对[dfn[x],dfn[x]+siz[x]-1]进行修改/询问即可

否则比较困难,需要先找到x为根的子树里,rt属于x的哪个儿子son。然后等价于整棵树操作后son子树内部反向操作一下。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100010;
int lowbit(int x){return x&(-x);}
ll read(){ll x;scanf("%lld",&x);return x;}
int n,dep[N],siz[N],bel[N],fa[N],dfn[N],tot,a[N];
int son[N];
vector<int>e[N];
ll c[N*4],lz[N*4];
void dfs(int x)
{
    siz[x]=1;
    for(auto y:e[x]){
        if(y==fa[x])
            continue;
        dep[y]=dep[x]+1;
        fa[y]=x;
        dfs(y);
        siz[x]+=siz[y];
    }
}
void dfs(int x,int chain)
{
    dfn[x]=++tot;bel[x]=chain;
    int k=0;
    for(auto y:e[x])
    {
        if(y==fa[x])continue;
        if(siz[y]>siz[k])
            k=y;
    }
    if(k)//如果不是叶子
    {
        son[x]=k;
        dfs(k,chain);
    }
    for(auto y:e[x])
    {
        if(y==fa[x]||y==k)continue;
        dfs(y,y);
    }
}
void pushdown(int x,int l,int mid,int r)
{
    if(lz[x])
    {
        c[x*2]=c[x*2]+lz[x]*(mid-l+1);
        c[x*2+1]=c[x*2+1]+lz[x]*(r-mid);
        lz[x*2]=lz[x*2]+lz[x];
        lz[x*2+1]=lz[x*2+1]+lz[x];
        lz[x]=0;
    }
}
ll ask(int x,int l,int r,int tl,int tr)
{
    if(tl<=l&&r<=tr)
        return c[x];
    ll t=0;
    int mid=(l+r)/2;
    pushdown(x,l,mid,r);
    if(tl<=mid)
        t=t+ask(x*2,l,mid,tl,tr);
    if(tr>mid)
        t=t+ask(x*2+1,mid+1,r,tl,tr);
    return t;
}
void add(int x,int l,int r,int tl,int tr,ll v)
{
    if(tl<=l&&r<=tr)
    {
        c[x]=c[x]+v*(r-l+1);
        lz[x]=lz[x]+v;
        return ;
    }
    int mid=(l+r)/2;
    pushdown(x,l,mid,r);
    if(tl<=mid)
        add(x*2,l,mid,tl,tr,v);
    if(tr>mid)
        add(x*2+1,mid+1,r,tl,tr,v);
    c[x]=c[x*2]+c[x*2+1];
}
ll lca_sum(int x,int y)
{
    ll summ=0;
    while(bel[x]!=bel[y])
    {
        if(dep[bel[x]]>dep[bel[y]])
        {
            summ=summ+ask(1,1,n,dfn[bel[x]],dfn[x]);
            x=fa[bel[x]];
        }
        else
        {
            summ=summ+ask(1,1,n,dfn[bel[y]],dfn[y]);
            y=fa[bel[y]];
        }
    }

    if(siz[x]>siz[y])
        summ=summ+ask(1,1,n,dfn[x],dfn[y]);
    else
        summ=summ+ask(1,1,n,dfn[y],dfn[x]);
    return summ;
}

void lca_add(int x,int y,int v)
{
    while(bel[x]!=bel[y])
    {
        if(dep[bel[x]]>dep[bel[y]])
        {
            add(1,1,n,dfn[bel[x]],dfn[x],v);
            x=fa[bel[x]];
        }
        else
        {
            add(1,1,n,dfn[bel[y]],dfn[y],v);
            y=fa[bel[y]];
        }
    }
    if(siz[x]>siz[y])
        add(1,1,n,dfn[x],dfn[y],v);
    else
        add(1,1,n,dfn[y],dfn[x],v);
}
int getson(int u,int v)
{
    while(bel[u]!=bel[v])
    {
        if(dep[bel[u]]<dep[bel[v]])
            swap(u,v);

        if(fa[bel[u]]==v)
            return bel[u];
        u=fa[bel[u]];
    }
    if(dep[u]<dep[v])
        return son[u];
    return son[v];
}
int main()
{
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    n=read();
    int rt=1;
    for(int i=1;i<=n;i++)
        a[i]=read();
    for(int i=2;i<=n;i++)
        e[read()].push_back(i);
    dep[rt]=1;
    dfs(rt);
    dfs(rt,rt);
    for(int i=1;i<=n;i++)
        add(1,1,n,dfn[i],dfn[i],a[i]);

    for(int m=read();m;m--)
    {
        int t=read();
        if(t==1)
            rt=read();
        else if(t==2)
        {
            int x=read(),y=read(),v=read();
            lca_add(x,y,v);
        }
        else if(t==3)
        {
            int x=read(),v=read();
            if(x==rt)//根=x
                add(1,1,n,1,n,v);
            else if(dfn[x]<=dfn[rt]&&dfn[rt]<=dfn[x]+siz[x]-1)//rt是x的儿子,等价于全体加,son子树减
            {
                add(1,1,n,1,n,v);
                int son=getson(x,rt);

                add(1,1,n,dfn[son],dfn[son]+siz[son]-1,-v);
            }
            else//rt不在x子树内,等价于x子树加
                add(1,1,n,dfn[x],dfn[x]+siz[x]-1,v);
        }
        else if(t==4)
        {
            int x=read(),y=read();
            cout<<lca_sum(x,y)<<'\n';
        }
        else
        {
            int x=read();
            if(x==rt)
                cout<<c[1]<<'\n';
            else if(dfn[x]<=dfn[rt]&&dfn[rt]<=dfn[x]+siz[x]-1)//rt是x的儿子,等价于全体减去son子树
            {
                int son=getson(x,rt);
                cout<<c[1]-ask(1,1,n,dfn[son],dfn[son]+siz[son]-1)<<'\n';
            }
            else
            {
                cout<<ask(1,1,n,dfn[x],dfn[x]+siz[x]-1)<<'\n';
            }
        }
    }
}
[树链剖分]模板题

「SDOI2014」旅行

单点颜色修改,单点权值修改,路径求和,路径求最大值,对每个颜色开一个动态开点线段树即可解决。从颜色c1转为c2时,不需要真的删掉,只需要在c1线段树里令权值变为0即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100010;
int lowbit(int x){return x&(-x);}
ll read(){ll x;scanf("%lld",&x);return x;}
int n,dep[N],siz[N],bel[N],fa[N],dfn[N],tot,c[N],w[N],rt[N],lc[N*20],rc[N*20];
int son[N];
vector<int>e[N];
ll maxx[N*20],summ[N*20];
void dfs(int x)
{
    siz[x]=1;
    for(auto y:e[x]){
        if(y==fa[x])
            continue;
        dep[y]=dep[x]+1;
        fa[y]=x;
        dfs(y);
        siz[x]+=siz[y];
    }
}
void dfs(int x,int chain)
{
    dfn[x]=++tot;bel[x]=chain;
    int k=0;
    for(auto y:e[x])
    {
        if(y==fa[x])continue;
        if(siz[y]>siz[k])
            k=y;
    }
    if(k)//如果不是叶子
    {
        son[x]=k;
        dfs(k,chain);
    }
    for(auto y:e[x])
    {
        if(y==fa[x]||y==k)continue;
        dfs(y,y);
    }
}
void add(int &x,int l,int r,int d,ll v)
{
    if(!x)
        x=++tot;
    if(l==r)
    {
        maxx[x]=summ[x]=v;
        return ;
    }
    int mid=(l+r)/2;
    if(d<=mid)
        add(lc[x],l,mid,d,v);
    else
        add(rc[x],mid+1,r,d,v);
    maxx[x]=max(maxx[lc[x]],maxx[rc[x]]);
    summ[x]=summ[lc[x]]+summ[rc[x]];
}
ll ask_sum(int x,int l,int r,int tl,int tr)
{
    if(!x)
        return 0;
    if(tl<=l&&r<=tr)
        return summ[x];
    int mid=(l+r)/2;
    ll t=0;
    if(tl<=mid)
        t+=ask_sum(lc[x],l,mid,tl,tr);
    if(tr>mid)
        t+=ask_sum(rc[x],mid+1,r,tl,tr);
    return t;
}
ll ask_max(int x,int l,int r,int tl,int tr)
{
    if(!x)
        return 0;
    if(tl<=l&&r<=tr)
        return maxx[x];
    int mid=(l+r)/2;
    ll t=0;
    if(tl<=mid)
        t=max(t,ask_max(lc[x],l,mid,tl,tr));
    if(tr>mid)
        t=max(t,ask_max(rc[x],mid+1,r,tl,tr));
    return t;
}
ll lca_sum(int x,int y)
{
    // cout<<x<<'?'<<y<<endl;
    ll summ=0,C=c[x];
    while(bel[x]!=bel[y])
    {
        if(dep[bel[x]]>dep[bel[y]])
        {
            summ=summ+ask_sum(rt[C],1,n,dfn[bel[x]],dfn[x]);
            x=fa[bel[x]];
        }
        else
        {
            summ=summ+ask_sum(rt[C],1,n,dfn[bel[y]],dfn[y]);
            y=fa[bel[y]];
        }
    }
    if(siz[x]>siz[y])
        summ=summ+ask_sum(rt[C],1,n,dfn[x],dfn[y]);
    else
        summ=summ+ask_sum(rt[C],1,n,dfn[y],dfn[x]);
    return summ;
}

ll lca_max(int x,int y)
{
    ll t=1,C=c[x];
    while(bel[x]!=bel[y])
    {
        if(dep[bel[x]]>dep[bel[y]])
        {
            t=max(t,ask_max(rt[C],1,n,dfn[bel[x]],dfn[x]));
            x=fa[bel[x]];
        }
        else
        {
            t=max(t,ask_max(rt[C],1,n,dfn[bel[y]],dfn[y]));
            y=fa[bel[y]];
        }
    }
    if(siz[x]>siz[y])
        t=max(t,ask_max(rt[C],1,n,dfn[x],dfn[y]));
    else
        t=max(t,ask_max(rt[C],1,n,dfn[y],dfn[x]));
    return t;
}
int main()
{
    // freopen("tree.in","r",stdin);
    // freopen("tree.out","w",stdout);
    n=read();
    int m=read();
    for(int i=1;i<=n;i++)
    {
        w[i]=read();
        c[i]=read();
    }
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read();
        e[x].push_back(y);
        e[y].push_back(x);
    }
    dep[1]=1;
    dfs(1);
    dfs(1,1);
    tot=0;
    for(int i=1;i<=n;i++)
        add(rt[c[i]],1,n,dfn[i],w[i]);
    for(;m;m--)
    {
        char s[5];
        scanf("%s",s);
        if(s[1]=='C')
        {
            int x=read(),C=read();
            if(C==c[x])
                continue;
            add(rt[c[x]],1,n,dfn[x],0);
            c[x]=C;
            add(rt[c[x]],1,n,dfn[x],w[x]);
        }
        else if(s[1]=='W')
        {
            int x=read();
            w[x]=read();
            add(rt[c[x]],1,n,dfn[x],w[x]);
        }
        else if(s[1]=='S')
        {
            int x=read(),y=read();
            cout<<lca_sum(x,y)<<'\n';
        }
        else
        {
            int x=read(),y=read();
            cout<<lca_max(x,y)<<'\n';
        }
    }
}
[SDOI2014]旅行

 [SDOI2011]染色

路径染色,询问路径上颜色数量

也是经典线段树,考虑维护区间左端点颜色lc和右端点颜色rc,则合并的时候新sum为b.sum+c.sum-(b.rc==c.lc)

算答案时,考虑x跳到bel[x]能收益那么多颜色段,但是如果fa[bel[x]]颜色等于bel[x],颜色段减一即可

最后在同一个重链上时,直接累加,不需要减了

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200010;
int lowbit(int x){return x&(-x);}
ll read(){ll x;scanf("%lld",&x);return x;}
int n,dep[N],siz[N],bel[N],fa[N],dfn[N],tot,nfd[N],c[N],lz[N*4];
vector<int>e[N];
struct node
{
    int lc,rc,sum;
}o[4*N];
node merge(node b,node c)
{
    return {b.lc,c.rc,b.sum+c.sum-(b.rc==c.lc)};
}
void pushdown(int x)
{
    if(lz[x])
    {
        lz[x*2]=lz[x*2+1]=lz[x];
        o[x*2]={lz[x],lz[x],1};
        o[x*2+1]={lz[x],lz[x],1};
        lz[x]=0;
    }
}
node ask(int x,int l,int r,int tl,int tr)//区间询问颜色数量
{
    if(tl<=l&&r<=tr)
        return o[x];
    pushdown(x);
    int mid=(l+r)/2;
    if(tr<=mid)
        return ask(x*2,l,mid,tl,tr);
    if(tl>mid)
        return ask(x*2+1,mid+1,r,tl,tr);
    return merge(ask(x*2,l,mid,tl,tr),ask(x*2+1,mid+1,r,tl,tr));
}
void cover(int x,int l,int r,int tl,int tr,int c)
{
    if(tl<=l&&r<=tr)
    {
        lz[x]=c;
        o[x]={c,c,1};
        return ;
    }
    pushdown(x);
    int mid=(l+r)/2;
    if(tl<=mid)
        cover(x*2,l,mid,tl,tr,c);
    if(tr>mid)
        cover(x*2+1,mid+1,r,tl,tr,c);
    o[x]=merge(o[x*2],o[x*2+1]);
}
void dfs(int x)
{
    siz[x]=1;
    for(auto y:e[x]){
        if(y==fa[x])
            continue;
        dep[y]=dep[x]+1;
        fa[y]=x;
        dfs(y);
        siz[x]+=siz[y];
    }
}
void dfs(int x,int chain)
{
    bel[x]=chain;
    dfn[x]=++tot;
    nfd[tot]=x;
    int k=0;
    for(auto y:e[x])
    {
        if(fa[x]==y)
            continue;
        if(siz[y]>siz[k])
            k=y;
    }
    if(k)//如果不是叶子
        dfs(k,chain);
    for(auto y:e[x])
    {
        if(y==k||y==fa[x])
            continue;
        dfs(y,y);
    }
}
int lca_sum(int x,int y)
{
    int t=0;
    while(bel[x]!=bel[y])
    {
        if(dep[bel[x]]>dep[bel[y]])
        {
            t+=ask(1,1,n,dfn[bel[x]],dfn[x]).sum;
            if(ask(1,1,n,dfn[bel[x]],dfn[bel[x]]).lc==ask(1,1,n,dfn[fa[bel[x]]],dfn[fa[bel[x]]]).lc)
                t--;
            x=fa[bel[x]];
        }
        else
        {
            t+=ask(1,1,n,dfn[bel[y]],dfn[y]).sum;
            if(ask(1,1,n,dfn[bel[y]],dfn[bel[y]]).lc==ask(1,1,n,dfn[fa[bel[y]]],dfn[fa[bel[y]]]).lc)
                t--;
            y=fa[bel[y]];
        }
    }
    if(siz[x]>siz[y])
        t+=ask(1,1,n,dfn[x],dfn[y]).sum;
    else
        t+=ask(1,1,n,dfn[y],dfn[x]).sum;
    return t;
}
void lca_cover(int x,int y,int c)
{
    while(bel[x]!=bel[y])
    {
        if(dep[bel[x]]>dep[bel[y]])
        {
            cover(1,1,n,dfn[bel[x]],dfn[x],c);
            x=fa[bel[x]];
        }
        else
        {
            cover(1,1,n,dfn[bel[y]],dfn[y],c);
            y=fa[bel[y]];
        }
    }
    if(siz[x]>siz[y])
        cover(1,1,n,dfn[x],dfn[y],c);
    else
        cover(1,1,n,dfn[y],dfn[x],c);
}
void build(int x,int l,int r)
{
    if(l==r)
    {
        o[x]={c[nfd[l]],c[nfd[l]],1};
        return ;
    }
    int mid=(l+r)/2;
    build(x*2,l,mid);
    build(x*2+1,mid+1,r);
    o[x]=merge(o[x*2],o[x*2+1]);
}
int main()
{
    n=read();int m=read();
    for(int i=1;i<=n;i++)
        c[i]=read();
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read();
        e[x].push_back(y);
        e[y].push_back(x);
    }
    dep[1]=1;
    dfs(1);
    dfs(1,1);
    build(1,1,n);
    for(;m;m--)
    {
        char c;
        cin>>c;
        if(c=='C'){
            int x=read(),y=read();
            lca_cover(x,y,read());
        }
        else
            cout<<lca_sum(read(),read())<<'\n';
    }
}
[SDOI2011]染色

jzyz2371 [NOI2015] 软件包管理器

不妨设安装过了的颜色是1,没安装过的颜色是0

install x需要输出x到1这个链的0的数量,然后x到1这个链染色为1

uninstall x需要输出以x为根的子树的1的数量,然后将这个子树染色为0

可以发现线段树懒标记维护区间0和1的数量即可,询问只需要询问0的数量,这样install在链上跳的时候直接+=,而uninstall时只需要用siz[x]-子树内1的数量即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100010;
int lowbit(int x){return x&(-x);}
ll read(){ll x;scanf("%lld",&x);return x;}
int n,dep[N],siz[N],bel[N],fa[N],dfn[N],tot,nfd[N],lz[N*4];
vector<int>e[N];
int sum0[N*4],sum1[N*4];
void pushdown(int x,int l,int mid,int r)
{
    if(lz[x]==-1)
        return ;
    lz[x*2]=lz[x*2+1]=lz[x];
    sum1[x*2]=mid-l+1;
    sum1[x*2+1]=r-mid;
    sum0[x*2]=0;
    sum0[x*2+1]=0;
    if(lz[x]==0)
        swap(sum1[x*2],sum0[x*2]),swap(sum1[x*2+1],sum0[x*2+1]);
    lz[x]=-1;
}
int ask(int x,int l,int r,int tl,int tr)//区间询问0的数量
{
    if(tl<=l&&r<=tr)
        return sum0[x];
    int mid=(l+r)/2;
    pushdown(x,l,mid,r);
    if(tr<=mid)
        return ask(x*2,l,mid,tl,tr);
    if(tl>mid)
        return ask(x*2+1,mid+1,r,tl,tr);
    return ask(x*2,l,mid,tl,tr)+ask(x*2+1,mid+1,r,tl,tr);
}
void cover(int x,int l,int r,int tl,int tr,int c)
{
    if(tl<=l&&r<=tr)
    {
        lz[x]=c;
        sum1[x]=r-l+1;
        sum0[x]=0;
        if(c==0)
            swap(sum1[x],sum0[x]);
        return ;
    }
    int mid=(l+r)/2;
    pushdown(x,l,mid,r);
    if(tl<=mid)
        cover(x*2,l,mid,tl,tr,c);
    if(tr>mid)
        cover(x*2+1,mid+1,r,tl,tr,c);
    sum1[x]=sum1[x*2]+sum1[x*2+1];
    sum0[x]=sum0[x*2]+sum0[x*2+1];
}
void dfs(int x)
{
    siz[x]=1;
    for(auto y:e[x]){
        dep[y]=dep[x]+1;
        dfs(y);
        siz[x]+=siz[y];
    }
}
void dfs(int x,int chain)
{
    bel[x]=chain;
    dfn[x]=++tot;
    nfd[tot]=x;
    int k=0;
    for(auto y:e[x])
    {
        if(siz[y]>siz[k])
            k=y;
    }
    if(k)//如果不是叶子
        dfs(k,chain);
    for(auto y:e[x])
    {
        if(y==k)
            continue;
        dfs(y,y);
    }
}
int lca(int x,int y)
{
    int t=0;
    while(bel[x]!=bel[y])
    {
        if(dep[bel[x]]>dep[bel[y]])
        {
            t+=ask(1,1,n,dfn[bel[x]],dfn[x]);
            cover(1,1,n,dfn[bel[x]],dfn[x],1);
            x=fa[bel[x]];
        }
        else
        {
            t+=ask(1,1,n,dfn[bel[y]],dfn[y]);
            cover(1,1,n,dfn[bel[y]],dfn[y],1);
            y=fa[bel[y]];
        }
    }
    if(siz[x]>siz[y]){
        t+=ask(1,1,n,dfn[x],dfn[y]);
        cover(1,1,n,dfn[x],dfn[y],1);
    }
    else{
        t+=ask(1,1,n,dfn[y],dfn[x]);
        cover(1,1,n,dfn[y],dfn[x],1);
    }
    return t;
}
int main()
{
    freopen("manager.in","r",stdin);
    freopen("manager.out","w",stdout);
    n=read();
    for(int i=2;i<=n;i++)
    {
        fa[i]=read()+1;
        e[fa[i]].push_back(i);
    }
    dep[1]=1;
    dfs(1);
    dfs(1,1);
    memset(lz,-1,sizeof(lz));
    lz[1]=0;//区间赋值为0
    sum0[1]=n;
    for(int m=read();m;m--)
    {
        string s;
        cin>>s;
        if(s=="install"){
            cout<<lca(1,read()+1)<<'\n';//链的0的数量顺带cover 1
        }
        else{
            int x=read()+1;
            //询问1的数量即可
            cout<<siz[x]-ask(1,1,n,dfn[x],dfn[x]+siz[x]-1)<<'\n';
            cover(1,1,n,dfn[x],dfn[x]+siz[x]-1,0);
        }
    }
}
[NOI2015] 软件包管理器

 

posted @ 2025-06-02 11:13  zzuqy  阅读(44)  评论(0)    收藏  举报