QTREE3 - Query on a tree again!
English | Vietnamese |
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;
}