发现近段时间光遇到树状数组啊。。。
http://poj.org/problem?id=3321
给定一棵树,其根节点始终为1,各节点值最开始均为1,然后有两种操作,一是查询该节点下面的节点总数,二是将该节点值反向(1变0,0变1)。。。
这题和前几天的hdu3887相似。。。都是利用了求逆序数的方法。。但此题用到了一个重新给节点制定序列的方法(有人叫做时间戳),反正就是按照后序遍历的方式将节点再编一个号,这样我们记录第一次进入这个节点时的编号b和离开这个节点时的编号c,则我们可以保证某个节点子树上的节点编号一定小于其编号,而且其我们维护的值就是[b, c]这个区间的和。。。
注意此题居然卡了stl。。。。最后自己写邻接表390ms过的。。。我还自己栈模拟了递归,貌似直接dfs也不会爆栈。。。
代码:
#include<vector>
#include<iostream>
using namespace std;
const int N=200010;
int n, q, sum[N], vis[N], b[N], c[N], cn, stk[N], a[N];
int first[N], en;
struct node
{
int v, next;
} edge[N];
int query(int i)
{
int tmp=0;
for(; i>0; i-=i&(-i))
tmp += sum[i];
return tmp;
}
void update(int i, int v)
{
for(; i<=n; i+=i&(-i))
sum[i] += v;
}
void dfs()
{
int i, j, sn;
for(i=0; i<=n; i++)
vis[i] = 0;
sn = 0;
cn = 1;
stk[sn++] = 1;
vis[1] = 1;
while(sn!=0)
{
i = stk[sn-1];
if(vis[i]==1)
{
b[i] = cn;
vis[i] = 2;
}
if(first[i]!=-1)
{
j = first[i];
if(vis[edge[j].v]==0)
{
stk[sn++] = edge[j].v;
vis[edge[j].v] = 1;
}
first[i] = edge[j].next;
}
else
{
c[i] = cn++;
sn--;
}
}
}
void add(int x, int y)
{
edge[en].v = y;
edge[en].next = first[x];
first[x] = en++;
}
int main()
{
int i, x, y, v;
char op[3];
while(scanf("%d", &n)!=EOF)
{
if(n==0)
return 0;
for(i=0; i<=2*n; i++)
first[i] = -1;
en = 0;
for(i=1; i<n; i++)
{
scanf("%d%d", &x, &y);
add(x, y);
add(y, x);
}
dfs();
for(i=0; i<=n; i++)
sum[i] = 0;
for(i=1; i<=n; i++)
{
update(i, 1);
a[i] = 1;
}
scanf("%d", &q);
while(q--)
{
scanf("%s %d", op, &v);
if(op[0]=='Q')
printf("%d\n", query(c[v])-query(b[v]-1));
else
{
if(a[c[v]]==1)
{
update(c[v], -1);
a[c[v]] = 0;
}
else
{
update(c[v], 1);
a[c[v]] = 1;
}
}
}
}
return 0;
}