hdu 5709 动态线段树+合并

来源:互联网 发布:360vr全景通源码 编辑:程序博客网 时间:2024/05/16 03:37

Claris loves painting very much, so he painted a tree with beautiful colors.

The tree is a rooted tree with nn nodes which are conveniently labeled by 1,2,…,n1,2,…,n. Its root is the 11-st node, and the ii-th node is painted with color cici. If ci=cjci=cj, then we think these two nodes have the same color.

We define depthidepthi as the distance between the ii-th node and the root, and simply, the distance between two adjacent nodes is always 11.

Standing in front of this beautiful tree, Claris comes up with mm questions.
In each question, there are two integers xx and dd, which means that Claris wants to know the number of different kinds of colors occur in SS, where S={v|v in x′s subtree and depthv≤depthx+d}S={v|v in x′s subtree and depthv≤depthx+d}.
Input
The first line of the input contains an integer T(1≤T≤500)T(1≤T≤500), denoting the number of test cases.

In every test case, there are two integers n(1≤n≤100000)n(1≤n≤100000) and m(1≤m≤100000)m(1≤m≤100000) in the first line, denoting the number of nodes and queries.
The second line contains nn integers, the ii-th integer ci(1≤ ci ≤n)ci(1≤ci≤n) denotes the color of the ii-th node.
The third line contains n−1n−1 integers, the ii-th integer fi + 1 ( 1 ≤ fi < i ) fi+1(1 ≤ fi < i) denotes the father of the i+1i+1-th node.
In the following mm lines, each line contains two integers x(1≤x≤n) x (1≤x≤n) and d ( 0 ≤ d < n ) d (0 ≤ d < n ), denoting each query.

The input has been encoded, if you read xx and dd, the real xx and dd are x⊕lastx⊕last and d⊕lastd⊕last, where lastlast is the answer of the previous query and ⊕⊕ means bitwise exclusive-or.

Notice :
If it is the first query in this test case, then last=0last=0.
It is guaranteed that ∑n ≤ 500000∑n ≤ 500000 and ∑m≤500000∑m≤500000.
Output
For each query output a single integer in a line, denoting the answer.
Sample Input
1
5 8
1 3 3 2 2
1 1 3 3
1 0
0 0
3 0
1 3
2 1
2 0
6 2
4 1
Sample Output
1
2
3
1
1
2
1
1

学的是大佬的博客,真是又短又好理解啊。。
利用对于每个点开两个线段树,一个T1下标是 深度,值是总的不同的颜色的数量,
一个T2记录的是所有的颜色的最低的深度。然后从下往上合并线段树,对于询问化 d为 dep[x]+d,就是从根能到的深度,且必须是子树,所以就可以从下往上合并了。
因为达到了1e5*1e5的空间复杂度,所以考虑动态开树,动态开树的更新左边的话,右边也要记得指向旧的结点(我写的时候就漏了这个)

对于线段树合并这是我第一次接触,相当于板子一样了
合并T1,把它和父亲合在一起并赋给父亲,就是每个点新开一个点,然后都开进去,如果其中一条链断了,那么直接相连就好。算出来的是有重复的每个点在所有深度的总数(其实只有比它的深度,但是动态开树不会浪费空间)

利用T2合并去重,T2也是存在的所有点都要更新,如果其中一个没有了,直接连上就好,如果都有的情况,v数组保留最小的深度,然后旧的所在深度在T1树上的总数就要-1.

#include <bits/stdc++.h>using namespace std;const int N = 100010;const int M = 10000010;int n,m;int c[N],f[N],d[N],v[M],T1[M],T2[M],ls[M],rs[M];const int BUF=30000000;  char Buf[BUF],*buf=Buf;  inline void read(int&a){for(a=0;*buf<48;buf++);while(*buf>47)a=a*10+*buf++-48;}//一句话解决 int tot;int insert(int pre,int l,int r,int d,int val){    int x=++tot;    v[x]=v[pre]+val;    if(l==r)        return x;    int mid=(l+r)>>1;    if(d<=mid) rs[x]=rs[pre],ls[x]=insert(ls[pre],l,mid,d,val);    else ls[x]=ls[pre],rs[x]=insert(rs[pre],mid+1,r,d,val);    return x;}int merge1(int x,int y,int l,int r){    if(!x||!y) return x+y;    int z=++tot;    v[z]=v[x]+v[y];    if(l==r) return z;    int mid=(l+r)>>1;    ls[z]=merge1(ls[x],ls[y],l,mid);    rs[z]=merge1(rs[x],rs[y],mid+1,r);    return z;}int merge2(int x,int y,int l,int r,int p){    if(!x||!y) return x+y;    int z=++tot;    if(l==r) {        if(v[x]<v[y]) v[z]=v[x],T1[p]=insert(T1[p],1,n,v[y],-1);        else v[z]=v[y],T1[p]=insert(T1[p],1,n,v[x],-1);        return z;    }    int mid=(l+r)>>1;    ls[z]=merge2(ls[x],ls[y],l,mid,p);    rs[z]=merge2(rs[x],rs[y],mid+1,r,p);    return z;}int res=0;void ask(int x,int l,int r,int d){    if(r<=d)     {        res+=v[x];        return ;    }    int mid=(l+r)>>1;    ask(ls[x],l,mid,d);    if(d>mid) ask(rs[x],mid+1,r,d);    return ;}int main(){    fread(Buf,1,BUF,stdin);    int t;    read(t);    while(t--){        tot=0;        res=0;        read(n);read(m);        for(int i=1;i<=n;i++)            read(c[i]);        for(int i=2;i<=n;i++)            read(f[i]);        for(int i=1;i<=n;i++)            d[i]=d[f[i]]+1;        for(int i=1;i<=n;i++)        {            T1[i]=insert(0,1,n,d[i],1);//对于深度为下标,值是所有颜色的总数            T2[i]=insert(0,1,n,c[i],d[i]);//对于颜色为下标,值是每个颜色的深度        }        for(int i=n;i>1;i--)        {            T1[f[i]]=merge1(T1[i],T1[f[i]],1,n);//T1的合并和深度没什么关系,所有用不到深度,只用合并数量总数就好            T2[f[i]]=merge2(T2[i],T2[f[i]],1,n,f[i]);//T2的合并利用要更新颜色所以利用了深度,而本来T2的线段树的值就是深度直接比较就好        }        while(m--)        {            int x,y;            read(x),read(y);            x^=res,y^=res;            y+=d[x];            if(y>n) y=n;            res=0;            ask(T1[x],1,n,y);            printf("%d\n",res );        }        res=0;    }}
原创粉丝点击