https://www.spoj.com/problems/QTREE3/
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 integers a 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.
题目大意:给一棵树,每个节点只能是黑色或白色,初始全为白色。有两种操作,第一种把第i个节点的颜色翻转,第二种输出从节点1到节点i的路径上的第一个黑色节点,若无输出-1。
思路:树链剖分,树状数组维护区间和。令0代表白色,1代表黑色,操作二即寻找从节点1到节点i的路径上第一个值为1的节点。那么就记录下从节点1到节点i的各个区间,从前向后遍历,若当前区间的元素之和大于1则在当前区间内二分找第一个节点,若遍历结束仍无满足题意的区间则输出-1。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define pr pair<int,int>
#define mp make_pair
using namespace std;
const int maxn=1e5+5;
int tree[maxn];
int siz[maxn];//子树大小
int son[maxn];//重儿子
int fa[maxn];//父节点
int deep[maxn];//深度
int top[maxn];//所在链链顶
int pos[maxn];//dfs序编号
int a[maxn];
int Index[maxn];
vector<int> vec[maxn];//存边
vector<pr> prc;//存区间
int n,m,tot=0;
void dfs1(int u,int f)//处理出重儿子 深度 父亲 子树大小等信息
{
siz[u]=1;
son[u]=0;
fa[u]=f;
deep[u]=deep[f]+1;
int to;
for(int i=0;i<vec[u].size();i++)
{
to=vec[u][i];
if(to!=f)
{
dfs1(to,u);
siz[u]+=siz[to];
if(siz[son[u]]<siz[to])
son[u]=to;
}
}
}
void dfs2(int u,int f,int k)//处理出 链顶 dfs序 映射后的值 等信息
{
top[u]=k;
pos[u]=++tot;
Index[tot]=u;
if(son[u])
dfs2(son[u],u,k);
int to;
for(int i=0;i<vec[u].size();i++)
{
to=vec[u][i];
if(to!=f&&to!=son[u])
dfs2(to,u,to);
}
}
inline int lowbit(int x)
{
return x&(-x);
}
void update(int i,int v)
{
for(;i<=n;i+=lowbit(i))
tree[i]+=v;
}
int query(int l)
{
int ans=0;
for(;l;l-=lowbit(l))
ans+=tree[l];
return ans;
}
void work(int u,int v)//计算出u到v的路径 映射后的各段区间
{
while(!prc.empty())
prc.pop_back();
while(top[u]!=top[v])
{
if(deep[top[u]]<deep[top[v]])
swap(u,v);
prc.push_back(mp(pos[top[u]],pos[u]));
u=fa[top[u]];
}
if(deep[u]>deep[v])
swap(u,v);
prc.push_back(mp(pos[u],pos[v]));
}
inline void prework()
{
scanf("%d%d",&n,&m);
int u,v;
for(int i=1;i<n;i++)
{
scanf("%d %d",&u,&v);
vec[u].push_back(v);
vec[v].push_back(u);
}
dfs1(1,0);
dfs2(1,0,1);
}
inline void mainwork()
{
int u,v;
for(int i=0;i<m;i++)
{
scanf("%d%d",&u,&v);
if(u==0)
{
if(a[v]==0)
{
a[v]=1;
update(pos[v],1);
}
else
{
a[v]=0;
update(pos[v],-1);
}
}
else
{
work(1,v);
int l=0,r=0,tmp,mid;
for(int i=prc.size()-1;i>=0;i--)
{
tmp=query(prc[i].second)-query(prc[i].first-1);
if(tmp>=1)
{
l=prc[i].first;
r=prc[i].second;
break;
}
}
if(l==r&&l==0)
printf("-1\n");
else
{
int temp=l-1;
while(l<=r)
{
mid=(l+r)>>1;
if(query(mid)-query(temp)>=1)
r=mid-1;
else
l=mid+1;
}
printf("%d\n",Index[l]);
}
}
}
}
int main()
{
prework();
mainwork();
return 0;
}