BZOJ3159决战——树链剖分+非旋转treap(平衡树动态维护dfs序)

题目描述

输入

第一行有三个整数N、M和R,分别表示树的节点数、指令和询问总数,以及X国的据点。

接下来N-1行,每行两个整数X和Y,表示Katharon国的一条道路。

接下来M行,每行描述一个指令或询问,格式见题目描述。

输出

对于每个询问操作,输出所求的值。

样例输入

5 8 1
1 2
2 3
3 4
4 5
Sum 2 4
Increase 3 5 3
Minor 1 4
Sum 4 5
Invert 1 3
Major 1 2
Increase 1 5 2
Sum 1 5

样例输出

0
0
6
3
19

提示

1<=N,M<=50000.且对于运送操作1<=W<=1000

 

链修改、查询链上点权和、查询链上最大最小点权,这些操作直接用树链剖分+线段树就能解决。

重点是链翻转,这显然不能用线段树这种静态数据结构,所以将线段树维护$dfs$序换成平衡树动态维护$dfs$序。

对于链翻转操作,我们将每一段重链在$dfs$序上的区间都从$treap$中断裂出来,然后将这些重链断裂出来的$treap$再合并到一起,将根节点打上翻转标记,然后再将这些重链合并成的$treap$断裂开分别插入回原来$dfs$序上的位置即可。

因为树链剖分是从下往上找重链,在$dfs$序上就是从后往前找区间,所以合并和分裂还原时都要倒着操作(也就是从$dfs$序上靠前的区间开始操作),这样才能保证每次找$dfs$序上对应位置是正确的。

注意在链修改打标记时要立即下传一层,否则查询时可能会有查询点仍有标记未下传的情况。

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<bitset>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
ll z;
ll ans;
int x,y;
int n,m;
int cnt;
int tot;
int res;
int rot;
int miku;
int root;
int a,b,c,e;
char ch[20];
int s[50010];
int t[50010];
int L[50010];
int R[50010];
int r[50010];
int f[50010];
int d[50010];
ll mn[50010];
ll mx[50010];
ll sum[50010];
ll num[50010];
int ls[50010];
int rs[50010];
int son[50010];
int top[50010];
int siz[50010];
int rev[50010];
int val[50010];
int to[100010];
int size[50010];
int head[50010];
int next[100010];
ll INF=1ll<<60;
void add(int x,int y)
{
    tot++;
    next[tot]=head[x];
    head[x]=tot;
    to[tot]=y;
}
void dfs(int x)
{
    d[x]=d[f[x]]+1;
    siz[x]=1;
    for(int i=head[x];i;i=next[i])
    {
        if(to[i]!=f[x])
        {
            f[to[i]]=x;
            dfs(to[i]);
            siz[x]+=siz[to[i]];
            if(siz[to[i]]>siz[son[x]])
            {
                son[x]=to[i];
            }
        }
    }
}
void dfs2(int x,int tp)
{
    top[x]=tp;
    s[x]=++res;
    if(son[x])
    {
        dfs2(son[x],tp);
    }
    for(int i=head[x];i;i=next[i])
    {
        if(to[i]!=f[x]&&to[i]!=son[x])
        {
            dfs2(to[i],to[i]);
        }
    }
}
int build()
{
    int rt=++cnt;
    size[rt]=1;
    mn[rt]=INF;
    r[rt]=rand();
    return rt;
}
void pushup(int rt)
{
    size[rt]=size[ls[rt]]+size[rs[rt]]+1;
    mn[rt]=val[rt];
    mx[rt]=val[rt];
    sum[rt]=val[rt];
    if(ls[rt])
    {
        mx[rt]=max(mx[rt],mx[ls[rt]]);
        mn[rt]=min(mn[rt],mn[ls[rt]]);
        sum[rt]+=sum[ls[rt]];
    }
    if(rs[rt])
    {
        mx[rt]=max(mx[rt],mx[rs[rt]]);
        mn[rt]=min(mn[rt],mn[rs[rt]]);
        sum[rt]+=sum[rs[rt]];
    }
}
void pushdown(int rt)
{
    if(num[rt])
    {
        num[ls[rt]]+=num[rt];
        num[rs[rt]]+=num[rt];
        sum[ls[rt]]+=size[ls[rt]]*num[rt];
        sum[rs[rt]]+=size[rs[rt]]*num[rt];
        mn[ls[rt]]+=num[rt];
        mn[rs[rt]]+=num[rt];
        mx[ls[rt]]+=num[rt];
        mx[rs[rt]]+=num[rt];
        val[ls[rt]]+=num[rt];
        val[rs[rt]]+=num[rt];
        num[rt]=0;
    }
    if(rev[rt])
    {
        rev[ls[rt]]^=1;
        rev[rs[rt]]^=1;
        rev[rt]^=1;
        swap(ls[rt],rs[rt]);
    }
}
int merge(int x,int y)
{
    pushdown(x);
    pushdown(y);
    if(!x||!y)
    {
        return x+y;
    }
    if(r[x]<r[y])
    {
        rs[x]=merge(rs[x],y);
        pushup(x);
        return x;
    }
    else
    {
        ls[y]=merge(x,ls[y]);
        pushup(y);
        return y;
    }
}
void split(int rt,int &x,int &y,int k)
{
    if(!rt)
    {
        x=y=0;
        return ;
    }
    pushdown(rt);
    if(size[ls[rt]]>=k)
    {
        y=rt;
        split(ls[rt],x,ls[y],k);
        pushup(rt);
    }
    else
    {
        x=rt;
        split(rs[rt],rs[x],y,k-size[ls[rt]]-1);
        pushup(rt);
    }
}
void change(int x,int y,ll z)
{
    while(top[x]!=top[y])
    {
        if(d[top[x]]<d[top[y]])
        {
            swap(x,y);
        }
        split(root,a,b,s[x]);
        split(a,a,c,s[top[x]]-1);
        num[c]+=z;
        sum[c]+=size[c]*z;
        mn[c]+=z;
        mx[c]+=z;
        val[c]+=z;
        root=merge(merge(a,c),b);
        x=f[top[x]];
    }
    if(d[x]>d[y])
    {
        swap(x,y);
    }
    split(root,a,b,s[y]);
    split(a,a,c,s[x]-1);
    num[c]+=z;
    sum[c]+=size[c]*z;
    mn[c]+=z;
    mx[c]+=z;
    val[c]+=z;
    root=merge(merge(a,c),b);
}
void rotate(int x,int y)
{
    res=0;
    while(top[x]!=top[y])
    {
        if(d[top[x]]<d[top[y]])
        {
            swap(x,y);
        }
        split(root,a,b,s[x]);
        split(a,a,c,s[top[x]]-1);
        t[++res]=c;
        L[res]=s[top[x]];
        R[res]=s[x];
        root=merge(a,b);
        x=f[top[x]];
    }
    if(d[x]>d[y])
    {
        swap(x,y);
    }
    split(root,a,b,s[y]);
    split(a,a,c,s[x]-1);
    t[++res]=c;
    L[res]=s[x];
    R[res]=s[y];
    root=merge(a,b);
    for(int i=res;i>=1;i--)
    {
        miku=merge(miku,t[i]);
    }
    rev[miku]^=1;
    for(int i=res;i>=1;i--)
    {
        split(miku,a,b,R[i]-L[i]+1);
        miku=b;
        split(root,c,e,L[i]-1);
        root=merge(merge(c,a),e);
    }
}
ll query_max(int x,int y)
{
    ans=0;
    while(top[x]!=top[y])
    {
        if(d[top[x]]<d[top[y]])
        {
            swap(x,y);
        }
        split(root,a,b,s[x]);
        split(a,a,c,s[top[x]]-1);
        ans=max(ans,mx[c]);
        root=merge(merge(a,c),b);
        x=f[top[x]];
    }
    if(d[x]>d[y])
    {
        swap(x,y);
    }
    split(root,a,b,s[y]);
    split(a,a,c,s[x]-1);
    ans=max(ans,mx[c]);
    root=merge(merge(a,c),b);
    return ans;
}
ll query_min(int x,int y)
{
    ans=INF;
    while(top[x]!=top[y])
    {
        if(d[top[x]]<d[top[y]])
        {
            swap(x,y);
        }
        split(root,a,b,s[x]);
        split(a,a,c,s[top[x]]-1);
        ans=min(ans,mn[c]);
        root=merge(merge(a,c),b);
        x=f[top[x]];
    }
    if(d[x]>d[y])
    {
        swap(x,y);
    }
    split(root,a,b,s[y]);
    split(a,a,c,s[x]-1);
    ans=min(ans,mn[c]);
    root=merge(merge(a,c),b);
    return ans;
}
ll query_sum(int x,int y)
{
    ans=0;
    while(top[x]!=top[y])
    {
        if(d[top[x]]<d[top[y]])
        {
            swap(x,y);
        }
        split(root,a,b,s[x]);
        split(a,a,c,s[top[x]]-1);
        ans+=sum[c];
        root=merge(merge(a,c),b);
        x=f[top[x]];
    }
    if(d[x]>d[y])
    {
        swap(x,y);
    }
    split(root,a,b,s[y]);
    split(a,a,c,s[x]-1);
    ans+=sum[c];
    root=merge(merge(a,c),b);
    return ans;
}
int main()
{
    scanf("%d%d%d",&n,&m,&rot);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }
    dfs(rot);
    dfs2(rot,rot);
    for(int i=1;i<=n;i++)
    {
        root=merge(root,build());
    }
    while(m--)
    {
        scanf("%s",ch);
        scanf("%d%d",&x,&y);
        if(ch[2]=='c')
        {
            scanf("%lld",&z);
            change(x,y,z);
        }
        else if(ch[2]=='m')
        {
            printf("%lld\n",query_sum(x,y));
        }
        else if(ch[2]=='j')
        {
            printf("%lld\n",query_max(x,y));
        }
        else if(ch[2]=='n')
        {
            printf("%lld\n",query_min(x,y));
        }
        else
        {
            rotate(x,y);
        }
    }
}

加强版:

现在考虑将翻转操作加强,去掉规定翻转路径为从上往下的直链的条件(即不给出树根)。

做法与之前的类似,我们将路径看成两条从上往下的直链(即分成$lca->x$与$lca->y$两部分),可以肯定一条链的$dfs$序一定在另一条链的$dfs$序之前,那么为了确保在取出第一条链时不影响第二条链的$dfs$序,我们先对整条链$dfs$序较大的一条链进行操作再取出另一条链。这里有一点需要注意的就是$lca$所在重链的分配:如果操作链不是直链,那么$lca$所在重链分配给dfs序较小的一条链;否则分配给$dfs$序较大的一条链。这样我们就得到了树上两个开头相连的序列,显然是无法直接将两个序列连到一起就翻转的。我们将一个序列先翻转再接在另一个序列的前面,然后将整个序列一起翻转,将翻转后的序列拆分成原来的两部分并将第一部分翻转回来,最后分别插入回原$treap$,同样为了保证第二个插入的链的$dfs$序是对的,要先插入$dfs$序较小的链。

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<bitset>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
int r[50010];
int ls[50010];
int rs[50010];
ll sum[50010];
int val[50010];
int num[50010];
int mx[50010];
int mn[50010];
int rev[50010];
int size[50010];
int sz[50010];
int son[50010];
int top[50010];
int dep[50010];
int f[50010];
int s[50010];
int dfn;
int l1[50010];
int len1[50010];
int l2[50010];
int len2[50010];
int s1,s2;
int n,m;
char ch[10];
int cnt;
int tot;
int head[50010];
int nex[100010];
int to[100010];
int q[100010];
int x,y,z;
int root;
void add_edge(int x,int y)
{
    tot++;
    nex[tot]=head[x];
    head[x]=tot;
    to[tot]=y;
}
void dfs(int x)
{
    size[x]=1;
    for(int i=head[x];i;i=nex[i])
    {
        if(to[i]!=f[x])
        {
            f[to[i]]=x;
            dep[to[i]]=dep[x]+1;
            dfs(to[i]);
            size[x]+=size[to[i]];
            if(size[to[i]]>size[son[x]])
            {
                son[x]=to[i];
            }
        }
    }
}
void dfs2(int x,int tp)
{
    top[x]=tp;
    s[x]=++dfn;
    q[dfn]=x;
    if(son[x])
    {
        dfs2(son[x],tp);
    }
    for(int i=head[x];i;i=nex[i])
    {
        if(to[i]!=f[x]&&to[i]!=son[x])
        {
            dfs2(to[i],to[i]);
        }
    }
}
int newnode(int x)
{
    int rt=++cnt;
    val[rt]=x;
    r[rt]=rand();
    size[rt]=1;
    mx[rt]=mn[rt]=x;
    sum[rt]=1ll*x;
    return rt;
}
void pushup(int rt)
{
    size[rt]=1;
    mx[rt]=mn[rt]=val[rt];
    sum[rt]=1ll*val[rt];
    if(ls[rt])
    {
        sum[rt]+=sum[ls[rt]];
        mx[rt]=max(mx[rt],mx[ls[rt]]);
        mn[rt]=min(mn[rt],mn[ls[rt]]);
        size[rt]+=size[ls[rt]];
    }
    if(rs[rt])
    {
        sum[rt]+=sum[rs[rt]];
        mx[rt]=max(mx[rt],mx[rs[rt]]);
        mn[rt]=min(mn[rt],mn[rs[rt]]);
        size[rt]+=size[rs[rt]];
    }
}
void flip(int rt)
{
    rev[rt]^=1;
    swap(ls[rt],rs[rt]);
}
void add(int rt,int value)
{
    num[rt]+=value;
    sum[rt]+=1ll*size[rt]*value;
    mx[rt]+=value;
    mn[rt]+=value;
    val[rt]+=value;
}
void pushdown(int rt)
{
    if(rev[rt])
    {
        rev[rt]=0;
        flip(ls[rt]);
        flip(rs[rt]);
    }
    if(num[rt])
    {
        add(ls[rt],num[rt]);
        add(rs[rt],num[rt]);
        num[rt]=0;
    }
}
int merge(int x,int y)
{
    if(!x||!y)
    {
        return x+y;
    }
    pushdown(x);
    pushdown(y);
    if(r[x]<r[y])
    {
        rs[x]=merge(rs[x],y);
        pushup(x);
        return x;
    }
    else
    {
        ls[y]=merge(x,ls[y]);
        pushup(y);
        return y;
    }
}
void split(int rt,int &x,int &y,int k)
{
    if(!rt)
    {
        x=y=0;
        return ;
    }
    pushdown(rt);
    if(size[ls[rt]]>=k)
    {
        y=rt;
        split(ls[rt],x,ls[y],k);
        pushup(rt);
    }
    else
    {
        x=rt;
        split(rs[rt],rs[x],y,k-size[ls[rt]]-1);
        pushup(rt);
    }
}
void change(int x,int y,int value)
{
    int a=0,b=0,c=0;
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]])
        {
            swap(x,y);
        }
        split(root,a,c,s[x]);
        split(a,a,b,s[top[x]]-1);
        add(b,value);
        root=merge(merge(a,b),c);
        x=f[top[x]];
    }
    if(dep[x]>dep[y])
    {
        swap(x,y);
    }
    split(root,a,c,s[y]);
    split(a,a,b,s[x]-1);
    add(b,value);
    root=merge(merge(a,b),c);
}
ll query_sum(int x,int y)
{
    int a=0,b=0,c=0;
    ll res=0ll;
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]])
        {
            swap(x,y);
        }
        split(root,a,c,s[x]);
        split(a,a,b,s[top[x]]-1);
        res+=sum[b];
        root=merge(merge(a,b),c);
        x=f[top[x]];
    }
    if(dep[x]>dep[y])
    {
        swap(x,y);
    }
    split(root,a,c,s[y]);
    split(a,a,b,s[x]-1);
    res+=sum[b];
    root=merge(merge(a,b),c);
    return res;
}
int query_max(int x,int y)
{
    int a=0,b=0,c=0;
    int res=0;
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]])
        {
            swap(x,y);
        }
        split(root,a,c,s[x]);
        split(a,a,b,s[top[x]]-1);
        res=max(res,mx[b]);
        root=merge(merge(a,b),c);
        x=f[top[x]];
    }
    if(dep[x]>dep[y])
    {
        swap(x,y);
    }
    split(root,a,c,s[y]);
    split(a,a,b,s[x]-1);
    res=max(res,mx[b]);
    root=merge(merge(a,b),c);
    return res;
}
int query_min(int x,int y)
{
    int a=0,b=0,c=0;
    int res=2147483647;
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]])
        {
            swap(x,y);
        }
        split(root,a,c,s[x]);
        split(a,a,b,s[top[x]]-1);
        res=min(res,mn[b]);
        root=merge(merge(a,b),c);
        x=f[top[x]];
    }
    if(dep[x]>dep[y])
    {
        swap(x,y);
    }
    split(root,a,c,s[y]);
    split(a,a,b,s[x]-1);
    res=min(res,mn[b]);
    root=merge(merge(a,b),c);
    return res;
}
void turn_round(int x,int y)
{
    int a=0,b=0,c=0,d=0;
    s1=s2=0;
    int fx=x,fy=y;
    while(top[x]!=top[y])
    {
        if(dep[top[x]]>dep[top[y]])
        {
            l1[++s1]=s[top[x]];
            len1[s1]=s[x];
            x=f[top[x]];
        }
        else
        {
            l2[++s2]=s[top[y]];
            len2[s2]=s[y];
            y=f[top[y]];
        }
    }
    if(dep[x]>dep[y])
    {
        swap(x,y);
    }
    if((s[fx]<s[fy]&&fx!=x)||(s[fx]>s[fy]&&fy==x))
    {
        l1[++s1]=s[x];
        len1[s1]=s[y];
    }
    else if((s[fx]<s[fy]&&fx==x)||(s[fx]>s[fy]&&fy!=x))
    {
        l2[++s2]=s[x];
        len2[s2]=s[y];
    }
    int rot1=0;
    int rot2=0;
    int leng=0;
    if(s[fx]<s[fy])
    {
        for(int i=1;i<=s2;i++)
        {
            split(root,a,c,len2[i]);
            split(a,a,b,l2[i]-1);
            root=merge(a,c);
            rot2=merge(b,rot2);
        }
        for(int i=1;i<=s1;i++)
        {
            split(root,a,c,len1[i]);
            split(a,a,b,l1[i]-1);
            root=merge(a,c);
            rot1=merge(b,rot1);
            leng+=len1[i]-l1[i]+1;
        }
        flip(rot1);
        rot1=merge(rot1,rot2);
        flip(rot1);
        split(rot1,a,b,leng);
        flip(a);
        rot1=a,rot2=b;
        for(int i=s1;i>=1;i--)
        {
            split(rot1,a,b,len1[i]-l1[i]+1);
            rot1=b;
            split(root,c,d,l1[i]-1);
            root=merge(merge(c,a),d);
        }
        for(int i=s2;i>=1;i--)
        {
            split(rot2,a,b,len2[i]-l2[i]+1);
            rot2=b;
            split(root,c,d,l2[i]-1);
            root=merge(merge(c,a),d);
        }
    }
    else
    {
        for(int i=1;i<=s1;i++)
        {
            split(root,a,c,len1[i]);
            split(a,a,b,l1[i]-1);
            root=merge(a,c);
            rot1=merge(b,rot1);
        }
        for(int i=1;i<=s2;i++)
        {
            split(root,a,c,len2[i]);
            split(a,a,b,l2[i]-1);
            root=merge(a,c);
            rot2=merge(b,rot2);
            leng+=len2[i]-l2[i]+1;
        }
        flip(rot2);
        rot2=merge(rot2,rot1);
        flip(rot2);
        split(rot2,a,b,leng);
        flip(a);
        rot2=a,rot1=b;
        for(int i=s2;i>=1;i--)
        {
            split(rot2,a,b,len2[i]-l2[i]+1);
            rot2=b;
            split(root,c,d,l2[i]-1);
            root=merge(merge(c,a),d);
        }
        for(int i=s1;i>=1;i--)
        {
            split(rot1,a,b,len1[i]-l1[i]+1);
            rot1=b;
            split(root,c,d,l1[i]-1);
            root=merge(merge(c,a),d);
        }
    }
}
int main()
{
    srand(12378);
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        add_edge(x,y);
        add_edge(y,x);
    }
    dfs(1);
    dfs2(1,1);
    for(int i=1;i<=n;i++)
    {
        root=merge(root,newnode(0));
    }
    while(m--)
    {
        scanf("%s%d%d",ch,&x,&y);
        if(ch[2]=='c')
        {
            scanf("%d",&z);
            change(x,y,z);
        }
        else if(ch[2]=='v')
        {
            turn_round(x,y);
        }
        else if(ch[2]=='m')
        {
            printf("%lld\n",query_sum(x,y));
        }
        else if(ch[2]=='j')
        {
            printf("%d\n",query_max(x,y));
        }
        else
        {
            printf("%d\n",query_min(x,y));
        }
    }
}

转载于:https://www.cnblogs.com/Khada-Jhin/p/9985945.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值