动态树LCT||树链剖分+线段树(SPOJ QTREE3 - Query on a tree again!)


QTREE3 - Query on a tree again!

no tags 

You are given a tree (an acyclic undirected connected graph) with N nodes. The tree nodes are numbered from 1 to N. In the start, the color of any node in the tree is white.

We will ask you to perfrom some instructions of the following form:

  • 0 i : change the color of the i-th node (from white to black, or from black to white);
    or
  • 1 v : ask for the id of the first black node on the path from node 1 to node v. if it doesn't exist, you may return -1 as its result.

Input

In the first line there are two integers N and Q.

In the next N-1 lines describe the edges in the tree: a line with two integersa b denotes an edge between a and b.

The next Q lines contain instructions "0 i" or"1 v" (1 ≤ i, v ≤ N).

Output

For each "1 v" operation, write one integer representing its result.

Example

Input:
9 8
1 2
1 3
2 4
2 9
5 9
7 9
8 9
6 8
1 3
0 8
1 6
1 7
0 2
1 9
0 2
1 9 

Output:
-1
8
-1
2
-1

Constraints & Limits

There are 12 real input files.

For 1/3 of the test cases, N=5000, Q=400000.

For 1/3 of the test cases, N=10000, Q=300000.

For 1/3 of the test cases, N=100000, Q=100000.


题意:时尚的节点有两种颜色(黑白),刚开始都是白的;有两种操作:第一种是将节点v的颜色取反,另一种是询问从1到v的路径上第一个黑点是谁

思路:LCT维护一个key表示当前节点额颜色,sum[v]表示以v为跟的子树总共有多少个黑节点

对于第一种操作,只需要对节点取反,第二种操作则先Access(v),然后在1到v形成的Splay上找到要求的节点就行了

#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=100010;
int N,Q;
int head[maxn],tot;
struct node
{
    int v,next;
}edge[maxn*2];

void add_edge(int u,int v)
{
    edge[tot].v=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}

struct LCT
{
    int ch[maxn][2],pre[maxn],key[maxn];
    int rev[maxn],sum[maxn];
    bool rt[maxn];
    void dfs(int u,int f)
    {
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].v;
            if(v==f)continue;
            pre[v]=u;
            dfs(v,u);
            //cout<<u<<" ";
        }
    }
    void update_rev(int r)
    {
        if(!r)return;
        swap(ch[r][0],ch[r][1]);
        rev[r]^=1;
    }
    void pushdown(int r)
    {
        if(rev[r])
        {
            update_rev(ch[r][1]);
            update_rev(ch[r][0]);
            rev[r]=0;
        }
    }
    void pushup(int r)
    {
        sum[r]=sum[ch[r][0]]+sum[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的两个儿子
    int lca(int &u,int &v)
    {
        Access(v),v=0;
        int ans=0;
        while(u)
        {
            Splay(u);
            if(!pre[u])return sum[ch[u][1]]+sum[v];
            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)
    {
        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(sum,0,sizeof(sum));
        tot=0;
        for(int i=0;i<=N;i++)rt[i]=true,key[i]=0;
    }
    void Change(int v)
    {
        Splay(v);
        key[v]^=1;
        pushup(v);
    }
    int Query(int v)
    {
        Access(v);
        Splay(v);
        //for(int i=1;i<=N;i++)cout<<key[i]<<" ";
        //cout<<endl;
        if(sum[v]==0)return -1;
        while(v)
        {
            if(sum[ch[v][0]]==0&&key[v]==1)return v;
            else if(sum[ch[v][0]])v=ch[v][0];
            else v=ch[v][1];
        }
        //return -1;
    }
}tree;
int main()
{
    int u,v;
    while(scanf("%d%d",&N,&Q)!=EOF)
    {
        tree.init();
        for(int i=1;i<N;i++)
        {
            scanf("%d%d",&u,&v);
            add_edge(u,v);
            add_edge(v,u);
        }
        tree.dfs(1,1);
        while(Q--)
        {
            scanf("%d%d",&u,&v);
            if(u==0)tree.Change(v);
            else printf("%d\n",tree.Query(v));
        }
    }
    return 0;
}


还可以用树链剖分做:线段树维护一个key表示区间内是不是有黑色节点,X表示区间内最靠上的黑色节点的编号,应为是查询1-v上的,所以以1为根,查询的时候只要查到就更新答案

#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=200010;
int head[maxn],tot;
int top[maxn];
int num[maxn];
int son[maxn];
int fa[maxn];
int w[maxn];
int fw[maxn];
int pos;
int deep[maxn];
int N,Q;
int id[maxn];
struct E
{
    int v,next;
}edge[maxn*2];
void init()
{
    pos=tot=0;
    memset(head,-1,sizeof(head));
    memset(son,-1,sizeof(son));
}
void add_edge(int u,int v)
{
    edge[tot].v=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}
void dfs1(int u,int f,int dep)
{
    deep[u]=dep;
    num[u]=1;fa[u]=f;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].v;
        if(v==f)continue;
        dfs1(v,u,dep+1);
        num[u]+=num[v];
        if(son[u]==-1||num[son[u]]<num[v])
            son[u]=v;
    }
}
void dfs2(int u,int sp)
{
    top[u]=sp;
    w[u]=++pos;
    fw[w[u]]=u;
    if(son[u]==-1)return;
    dfs2(son[u],sp);
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].v;
        if(v!=fa[u]&&v!=son[u])
            dfs2(v,v);
    }
}
struct IntervalTree
{
    int key[maxn<<2];
    int X[maxn<<2];
    void build(int o,int l,int r)
    {
        key[o]=X[o]=0;
        if(l==r)return;
        int mid=(l+r)>>1;
        build(o<<1,l,mid);
        build(o<<1|1,mid+1,r);
    }
    void pushup(int o)
    {
        if(key[o<<1])
        {
            key[o]=key[o<<1];
            X[o]=X[o<<1];
        }
        else
        {
            key[o]=key[o<<1|1];
            X[o]=X[o<<1|1];
        }
    }
    void update(int o,int l,int r,int pos)
    {
        if(l==r)
        {
            key[o]^=1;
            if(key[o])X[o]=id[l];
            else X[o]=0;
            return;
        }
        int mid=(l+r)>>1;
        if(pos<=mid)update(o<<1,l,mid,pos);
        else update(o<<1|1,mid+1,r,pos);
        pushup(o);
    }
    int query(int o,int l,int r,int q1,int q2)
    {
        if(key[o]==0)return 0;
        if(q1<=l&&r<=q2)return X[o];
        int mid=(l+r)>>1;

        if(q2<=mid)return query(o<<1,l,mid,q1,q2);
        else if(q1>mid)return query(o<<1|1,mid+1,r,q1,q2);
        else
        {
            int tmp=query(o<<1,l,mid,q1,q2);
            if(tmp)return tmp;
            return query(o<<1|1,mid+1,r,q1,q2);
        }
    }
}tree;
int Query(int v)
{
    int u=1;
    int f1=top[u],f2=top[v];
    int ans=-1;
    while(f1!=f2)
    {
        if(deep[f1]<deep[f2])
        {
            swap(u,v);
            swap(f1,f2);
        }
        int tmp=tree.query(1,1,N,w[f1],w[u]);
        if(tmp)ans=tmp;
        u=fa[f1],f1=top[u];
    }
    if(deep[u]>deep[v])swap(u,v);
    int tmp=tree.query(1,1,N,w[u],w[v]);
    if(tmp)ans=tmp;
    return ans;
}
int main()
{
    int u,v;
    while(scanf("%d%d",&N,&Q)!=EOF)
    {
        init();
        for(int i=1;i<N;i++)
        {
            scanf("%d%d",&u,&v);
            add_edge(u,v);
            add_edge(v,u);
        }
        dfs1(1,1,1);
        dfs2(1,1);
        for(int i=1;i<=N;i++)id[w[i]]=i;
        tree.build(1,1,N);
        while(Q--)
        {
            scanf("%d%d",&u,&v);
            if(!u)tree.update(1,1,N,w[v]);
            else printf("%d\n",Query(v));
        }
    }
    return 0;
}



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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值