动态树hdu4010 - Query on The Trees

Query on The Trees

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65768/65768 K (Java/Others)
Total Submission(s): 3027    Accepted Submission(s): 1380


Problem Description
We have met so many problems on the tree, so today we will have a query problem on a set of trees.
There are N nodes, each node will have a unique weight Wi. We will have four kinds of operations on it and you should solve them efficiently. Wish you have fun!

 

Input
There are multiple test cases in our dataset.
For each case, the first line contains only one integer N.(1 ≤ N ≤ 300000) The next N‐1 lines each contains two integers x, y which means there is an edge between them. It also means we will give you one tree initially.
The next line will contains N integers which means the weight Wi of each node. (0 ≤ Wi ≤ 3000)
The next line will contains an integer Q. (1 ≤ Q ≤ 300000) The next Q lines will start with an integer 1, 2, 3 or 4 means the kind of this operation.
1. Given two integer x, y, you should make a new edge between these two node x and y. So after this operation, two trees will be connected to a new one.
2. Given two integer x, y, you should find the tree in the tree set who contain node x, and you should make the node x be the root of this tree, and then you should cut the edge between node y and its parent. So after this operation, a tree will be separate into two parts.
3. Given three integer w, x, y, for the x, y and all nodes between the path from x to y, you should increase their weight by w.
4. Given two integer x, y, you should check the node weights on the path between x and y, and you should output the maximum weight on it.
 

Output
For each query you should output the correct answer of it. If you find this query is an illegal operation, you should output ‐1.
You should output a blank line after each test case.
 

Sample Input
  
  
5 1 2 2 4 2 5 1 3 1 2 3 4 5 6 4 2 3 2 1 2 4 2 3 1 3 5 3 2 1 4 4 1 4
 

Sample Output
 
 
3 -1 7
Hint
We define the illegal situation of different operations: In first operation: if node x and y belong to a same tree, we think it's illegal. In second operation: if x = y or x and y not belong to a same tree, we think it's illegal. In third operation: if x and y not belong to a same tree, we think it's illegal. In fourth operation: if x and y not belong to a same tree, we think it's illegal.

下面的话摘自动态树LCT总结

首先说一下什么是动态树。动态树可以维护一个动态的森林,支持树的合并(两棵合并成一棵),分离(把某个点和它父亲点分开),动态LCA,树上的点权和边权维护、查询(单点或者树上的一条路径),换根。

access是所有动态树所有操作的基础,一次access(u)操作会把从点u到根上的所有点按深度用一棵splay维护,左边比根深度小,右边比根大,那么要提取x到y的路径只要把x换成根再access(y)就可以,换根操作可以先access(x),splay(x)再把整棵splay翻转,因为splay(x)以后在splay上x的右子树是空的,翻转以后x就是深度最小的点,也就是根,这里的翻转可以打懒惰标记,同样,维护链上的和、极值也可以用同样的方法维护。

这样的话所有操作就是先access,再splay,打标记


刚开始不太明白动态树的这个几个操作,做道题(基本上就是照的大神的模板抄的)之后感觉清楚多了

题意:树的集合支持下面四中操作

1.在x,y之间连一条边

2.把x变成树的跟,将y与其父亲分离

3.将x到y路径上的点权值加上w

4.询问x到y路径上权值最大的点的权值

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;

const int maxn=300010;
const int INF=1000000000;

int N,Q;
struct Node
{
    Node *p,*ch[2];
    int mx,rev,val,add;
}node[maxn],*cur,*null;
Node *NewNode(int key)
{
    cur->p=cur->ch[0]=cur->ch[1]=null;
    cur->mx=cur->val=key;
    cur->rev=0;
    return cur++;
}
void init()
{
    null=node;
    null->p=null->ch[0]=null->ch[1]=null;
    null->mx=null->val=-INF;
    null->add=0;
    null->rev=0;
    cur=node+1;
}
struct LCT
{
    bool isroot(Node *x)
    {
        return x==null||x->p->ch[0]!=x&&x->p->ch[1]!=x;
    }
    void pushup(Node *x)
    {
        x->mx=max(x->val,max(x->ch[0]->mx,x->ch[1]->mx));
    }
    void pushdown(Node *x)
    {
        if(x==null)return;
        if(x->rev)
        {
            x->rev=0;
            if(x->ch[0]!=null)x->ch[0]->rev^=1;
            if(x->ch[1]!=null)x->ch[1]->rev^=1;
            swap(x->ch[0],x->ch[1]);
        }
        if(x->add)
        {
            if(x->ch[0]!=null) x->ch[0]->add+=x->add,x->ch[0]->val+=x->add,x->ch[0]->mx+=x->add;
            if(x->ch[1]!=null) x->ch[1]->add+=x->add,x->ch[1]->val+=x->add,x->ch[1]->mx+=x->add;
            x->add=0;
        }
    }
    void rotate(Node *x,int f)
    {
        if(isroot(x)) return;
        Node *y=x->p;
        y->ch[!f]=x->ch[f];
        x->p=y->p;
        if(x->ch[f]!=null) x->ch[f]->p=y;
        if(y!=null)
        {
            if(y==y->p->ch[1]) y->p->ch[1]=x;
            else if(y==y->p->ch[0]) y->p->ch[0]=x;
        }
        x->ch[f]=y;
        y->p=x;
        pushup(y);
    }
    void Splay(Node *x)
    {
        static Node *sta[maxn];
        int top=1;
        sta[0]=x;
        for(Node *y=x;!isroot(y);y=y->p)
            sta[top++]=y->p;
        while (top) pushdown(sta[--top]);
        while (!isroot(x))
        {
            Node *y=x->p;
            if(isroot(y)) rotate(x,x==y->ch[0]);
            else
            {
                int f=y->p->ch[0]==y;
                if(y->ch[f]==x) rotate(x,!f);
                else rotate(y,f);
                rotate(x,f);
            }
        }
        pushup(x);
    }
    Node *access(Node *u)
    {
        Node *v=null;
        while (u!=null)
        {
            Splay(u);
            v->p=u;
            u->ch[1]=v;
            pushup(u);
            v=u;
            u=u->p;
        }
        return v;
    }
    Node *link(Node *u,Node *v)//合并
    {
        access(u);
        Splay(u);
        u->rev=1;
        u->p=v;
    }
    Node *cut(Node *u)//分离
    {
        access(u);
        Splay(u);
        u->ch[0]=u->ch[0]->p=null;
        pushup(u);
    }
    void changeroot(Node *u)
    {
        access(u)->rev^=1;
    }
    Node *getroot(Node *u)//找根
    {
        access(u);
        Splay(u);
        while (u->p!=null) u=u->p;
        Splay(u);
        return u;
    }
    bool judge(Node *u,Node *v)//判断是否在同一子树
    {
        while (u->p!=null) u=u->p;
        while (v->p!=null) v=v->p;
        return u==v;
    }
}tree;
struct Edge
{
    int v,u;
}edge[maxn];
int main()
{
    while(scanf("%d",&N)!=EOF)
    {
        init();
        for(int i=1;i<N;i++)
            scanf("%d%d",&edge[i].u,&edge[i].v);
        for(int i=1;i<=N;i++)
        {
            int x;
            scanf("%d",&x);
            NewNode(x);
        }
        for(int i=1;i<N;i++)
            tree.link(node+edge[i].u,node+edge[i].v);
        scanf("%d",&Q);
        int op,x,y,w;
        while(Q--)
        {
            scanf("%d%d%d",&op,&x,&y);
            if(op==1)
            {
                if(tree.judge(node+x,node+y))
                {
                    printf("-1\n");
                    continue;
                }
                tree.link(node+x,node+y);
            }
            else if(op==2)
            {
                if(x==y||!tree.judge(node+x,node+y))
                {
                    printf("-1\n");
                    continue;
                }
                tree.changeroot(node+x);
                tree.cut(node+y);
            }
            else if(op==3)
            {
                scanf("%d",&w);
                if(!tree.judge(node+y,node+w))
                {
                    printf("-1\n");
                    continue;
                }
                tree.changeroot(node+y);
                tree.access(node+w);
                Node *tmp=tree.getroot(node+w);
                tree.Splay(tmp);
                tmp->add+=x;
                tmp->mx+=x;
                tmp->val+=x;
            }
            else
            {
                if(!tree.judge(node+x,node+y))
                {
                    printf("-1\n");
                    continue;
                }
                tree.changeroot(node+x);
                tree.access(node+y);
                Node *tmp=tree.getroot(node+y);
                tree.Splay(tmp);
                printf("%d\n",tmp->mx);
            }
        }
        printf("\n");
    }
    return 0;
}


数组版:

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
const int maxn=300010;
const int INF=1000000000;
int ch[maxn][2],pre[maxn],key[maxn];
int add[maxn],rev[maxn],maxv[maxn];
bool rt[maxn];
int N,Q;
struct Edge
{
    int v,next;
}edge[maxn*2];
int head[maxn],tot;
void add_edge(int u,int v)
{
    edge[tot].v=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}
void dfs(int u)
{
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].v;
        if(pre[v]!=0)continue;
        pre[v]=u;
        dfs(v);
    }
}
void update_add(int r,int d)
{
    if(!r)return ;
    key[r]+=d;
    add[r]+=d;
    maxv[r]+=d;
}
void update_rev(int r)
{
    if(!r)return;
    swap(ch[r][0],ch[r][1]);
    rev[r]^=1;
}
void pushdown(int r)
{
    if(add[r])
    {
        update_add(ch[r][0],add[r]);
        update_add(ch[r][1],add[r]);
        add[r]=0;
    }
    if(rev[r])
    {
        update_rev(ch[r][1]);
        update_rev(ch[r][0]);
        rev[r]=0;
    }
}
void pushup(int r)
{
    maxv[r]=max(max(maxv[ch[r][0]],maxv[ch[r][1]]),key[r]);
}
void rotate(int x)
{
    int y=pre[x],kind=ch[y][1]==x;
    ch[y][kind]=ch[x][!kind];
    pre[ch[y][kind]]=y;
    pre[x]=pre[y];
    pre[y]=x;
    ch[x][!kind]=y;
    if(rt[y])rt[y]=false,rt[x]=true;
    else ch[pre[x]][ch[pre[x]][1]==y]=x;
    pushup(y);
}
//将根节点到r的路径上的所有及诶单的标记下方
void P(int r)
{
    if(!rt[r])P(pre[r]);
    pushdown(r);
}
void Splay(int r)
{
    P(r);
    while(!rt[r])
    {
        int f=pre[r],ff=pre[f];
        if(rt[f])rotate(r);
        else if((ch[ff][1]==f)==(ch[f][1]==r))
            rotate(f),rotate(r);
        else rotate(r),rotate(r);
    }
    pushup(r);
}
int Access(int x)
{
    int y=0;
    for(;x;x=pre[y=x])
    {
        Splay(x);
        rt[ch[x][1]]=true,rt[ch[x][1]=y]=false;
        pushup(x);
    }
    return y;
}
 int getroot(int x)
 {
    Access(x);
    Splay(x);
    while (ch[x][0])
        x = ch[x][0];
    return x;
}
//判断是否同根
bool judge(int u,int v)
{
    while(pre[u])u=pre[u];
    while(pre[v])v=pre[v];
    return u==v;
}
//将r变成他所在根
void mroot(int r)
{
    Access(r);
    Splay(r);
    update_rev(r);
}
//调用后u是原来u和v的lca,v和ch[u][1]分别存折lca的两个儿子
void lca(int &u,int &v)
{
    Access(v),v=0;
    while(u)
    {
        Splay(u);
        if(!pre[u])return;
        rt[ch[u][1]]=true;
        rt[ch[u][1]=v]=false;
        pushup(u);
        u=pre[v=u];
    }
}
//将u合并到v上
void link(int u,int v)
{
    mroot(u);
    pre[u]=v;
}
//将v和他的父节点分离
void cut(int v)
{
    //mroot(v);
    Access(v);
    Splay(v);
    pre[ch[v][0]]=0;
    pre[v]=0;
    rt[ch[v][0]]=true;
    ch[v][0]=0;
    pushup(v);
}
void init()
{
    memset(head,-1,sizeof(head));
    memset(pre,0,sizeof(pre));
    memset(ch,0,sizeof(ch));
    memset(rev,0,sizeof(rev));
    memset(add,0,sizeof(add));
    for(int i=0;i<=N;i++)rt[i]=true;
    maxv[0]=-INF;
}
int main()
{
    while(scanf("%d",&N)!=EOF)
    {
        init();
        for(int i=1;i<N;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            add_edge(u,v);
            add_edge(v,u);
        }
        for(int i=1;i<=N;i++)
        {
            int x;
            scanf("%d",&key[i]);
            maxv[i]=key[i];
        }
        pre[1]=-1;
        dfs(1);
        pre[1]=0;
        scanf("%d",&Q);
        int op,x,y,w;
        while(Q--)
        {
            scanf("%d%d%d",&op,&x,&y);
            if(op==1)
            {
                if(judge(x,y))
                {
                    printf("-1\n");
                    continue;
                }
                link(x,y);
            }
            else if(op==2)
            {
                if(x==y||!judge(x,y))
                {
                    printf("-1\n");
                    continue;
                }
                mroot(x);
                cut(y);
            }
            else if(op==3)
            {
                scanf("%d",&w);
                if(!judge(y,w))
                {
                    printf("-1\n");
                    continue;
                }
                mroot(y);
                Access(w);
                int tmp=getroot(w);
                Splay(tmp);
                add[tmp]+=x;
                maxv[tmp]+=x;
                key[tmp]+=x;
            }
            else
            {
                if(!judge(x,y))
                {
                    printf("-1\n");
                    continue;
                }
                mroot(x);
                Access(y);
                int tmp=getroot(y);
                Splay(tmp);
                printf("%d\n",maxv[tmp]);
            }
        }
        printf("\n");
    }
    return 0;
}




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值