• SPOJ QTREE Query on a tree VI


    You are given a tree (an acyclic undirected connected graph) with n nodes. The tree nodes are numbered from 1 to n. Each node has a color, white or black. All the nodes are black initially. We will ask you to perform some instructions of the following form:

    • 0 u: ask for how many nodes are connected to u, two nodes are connected if all the node on the path from u to v (inclusive u and v) have the same color.
    • 1 u: toggle the color of u (that is, from black to white, or from white to black).

    Input

    The first line contains a number n that denotes the number of nodes in the tree (1 ≤ n ≤ 105). In each of the following n-1 lines, there will be two numbers (uv) that describes an edge of the tree (1 ≤ uv ≤ n). The next line contains a numberm denoting number of operations we are going to process (1 ≤ m ≤ 105). Each of the following m lines describe an operation (tu) as we mentioned above(0 ≤ t ≤ 1, 1 ≤ u ≤ n).

    Output

    For each query operation, output the corresponding result.

    Example

    Input 1:
    5 1 2 1 3 1 4 1 5 3 0 1 1 1 0 1 Output 1:
    5 1

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

    Output 2:

    7 3 3


    Warning: large input/output data,be careful with certain languages

    改变某个点的颜色,或者询问与某点相连(两点路径上没有异色点)的同色点有多少个

    树 树链剖分+树状数组

    首先需要一个方便的统计答案的方式。每个点开一个统计答案的变量,显然可行,但是更新麻烦。

    考虑把一整块儿同色点的答案存储在它们中深度最浅的那个点上。若如此做,修改一个点的颜色时,只需要修改从该点到根的一条链上的答案。

    如果有了树剖,存储答案就可以用线段树或者树状数组。听说线段树容易T,那就用树状数组咯。

    修改一个点时,先找到从该点向上的最长同色链,整链减去答案,再改变颜色,再找到从该点向上的最长同色链,整链累加答案。

    如何找最长同色链?

      另开一个树状数组,记录链上某颜色点的数量的前缀和。如果整条重链上的某颜色点数量等于链长度,显然可以尝试继续上溯,否则说明同色链的最浅点在当前重链上,那么就在当前重链上二分判断。

    如何记录答案?

      脑洞了各种方法,最后发现比较方便的改法是,修改x的father到最浅点的father这条链(在前者上减去,在后者上累加),因为x上要存x以下的连通块的答案,方便颜色变回来时的累加,所以不能改。作为等价调整,需要在x的father上减。(类比DFS序问题的值维护)

    无数次WA和RE,过程中重构了两次代码,无尽的debug。写这一道题花了近一整天时间。

    前两份代码中,为了树状数组操作方便,树剖出的结点编号是倒着编号的。然而这样似乎在向下修改子结点时下标会出现0,使得树状数组爆炸。

    最后借鉴别人的二分方式,并改成了正序编号,迷之解决了迷之问题

    还有其他各种各样的毛病……

    顺带一提,最后两小时时间找出的bug,是某一句调用树状数组的时候,把pos和co的位置写反了……

    信心尽失,痛不欲生,好在最后还是过了。

      1 /*by SilverN*/
      2 #include<algorithm>
      3 #include<iostream>
      4 #include<cstring>
      5 #include<cstdio>
      6 #include<cmath>
      7 using namespace std;
      8 const int INF=1e8;
      9 const int mxn=200010;
     10 int read(){
     11     int x=0,f=1;char ch=getchar();
     12     while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
     13     while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
     14     return x*f;
     15 }
     16 struct edge{int v,nxt;}e[mxn<<1];
     17 int hd[mxn],mct=0;
     18 void add_edge(int u,int v){
     19     e[++mct].v=v;e[mct].nxt=hd[u];hd[u]=mct;return;
     20 }
     21 int n,m;
     22 //
     23 int ct[4][mxn];
     24 int col[mxn];
     25 void add(int p,int co,int v){
     26     while(p<=n){ct[co][p]+=v;p+=p&-p;}
     27     return;
     28 }
     29 int ask(int co,int p){
     30     int res=0;
     31     while(p){res+=ct[co][p];p-=p&-p;}
     32     return res;
     33 }
     34 //
     35 struct node{
     36     int fa,son,top;int sz,w,e;
     37 }t[mxn];
     38 int dep[mxn];
     39 int id[mxn],cnt=0;
     40 void DFS1(int u,int fa){
     41     t[u].sz=1;dep[u]=dep[fa]+1;
     42     for(int i=hd[u];i;i=e[i].nxt){
     43         if(e[i].v==fa)continue;int v=e[i].v;
     44         t[v].fa=u;
     45         DFS1(v,u);
     46         t[u].sz+=t[v].sz;
     47         if(t[v].sz>t[t[u].son].sz)t[u].son=v;
     48     }
     49     return;
     50 }
     51 void DFS2(int u,int top){
     52     t[u].w=++cnt;t[u].top=top;
     53     id[cnt]=u;
     54     if(t[u].son){
     55         DFS2(t[u].son,top);
     56         for(int i=hd[u];i;i=e[i].nxt){
     57             if(e[i].v==t[u].fa || e[i].v==t[u].son)continue;
     58             DFS2(e[i].v,e[i].v);
     59         }
     60     }
     61     t[u].e=cnt;
     62 }
     63 //
     64 int find(int L,int R,int co){//链上查询相连的同色最浅点 
     65     int l=L,r=R,ans=0;
     66     while(r<=l){//左边深右边浅 
     67         int mid=(l+r)>>1;
     68         int res=ask(co,l)-ask(co,mid-1);
     69         if(res==l-mid+1){
     70             ans=mid;l=mid-1;
     71         }else r=mid+1;
     72     }
     73     return ans;
     74 }
     75 int query(int x){//查询从x到y的链上的连续同色最浅点的树剖编号 
     76     int cc=col[x];
     77     while(t[x].top!=1){
     78         int tmp=ask(cc,t[x].w)-ask(cc,t[t[x].top].w-1);
     79         if(tmp==t[x].w-t[t[x].top].w+1){
     80             if(col[x]!=col[t[t[x].top].fa])return t[t[x].top].w;
     81             else x=t[t[x].top].fa;
     82         }
     83         else return find(t[x].w,t[t[x].top].w,cc);
     84     }
     85     return find(t[x].w,t[1].w,cc);
     86 }
     87 void upd(int x,int y,int v,int c){
     88     while(t[x].top!=t[y].top){
     89         if(dep[t[x].top]<dep[t[y].top])swap(x,y);
     90         add(t[t[x].top].w,c,v);
     91         add(t[x].w+1,c,-v);
     92         x=t[t[x].top].fa;
     93     }
     94     if(dep[x]>dep[y])swap(x,y);
     95     add(t[x].w,c,v);
     96     add(t[y].w+1,c,-v);
     97     return;
     98 }
     99 void update(int x){
    100     int y=id[query(x)];
    101     if(x>1)upd(t[y].fa,t[x].fa,-ask(col[x]+2,t[x].w),col[x]+2);
    102     add(t[x].w,col[x],-1);
    103     col[x]^=1;
    104     add(t[x].w,col[x],1);
    105     y=id[query(x)];
    106     if(x>1)upd(t[y].fa,t[x].fa,ask(col[x]+2,t[x].w),col[x]+2);
    107     return;
    108 }
    109 int main(){
    110 //    freopen("in.txt","r",stdin);
    111     int i,j,u,v;
    112     n=read();
    113     for(i=1;i<n;i++){
    114         u=read();v=read();
    115         add_edge(u,v);
    116         add_edge(v,u);
    117     }
    118     DFS1(1,1);t[1].fa=1;//
    119 //    cnt=n+1;
    120     DFS2(1,1);
    121     for(i=1;i<=n;i++){//初始化,全为黑色 
    122         col[i]=1;
    123         add(t[i].w,col[i]+2,t[i].sz);
    124         add(t[i].w+1,col[i]+2,-t[i].sz);
    125         add(t[i].w,col[i],1);
    126     }
    127     add(1,2,1);
    128     m=read();int op,x;
    129     while(m--){
    130         op=read();x=read();
    131         if(op){//修改 
    132             update(x);
    133         }
    134         else{//查询 
    135             int y=id[query(x)];
    136             int ans=ask(col[x]+2,t[y].w);
    137             printf("%d
    ",ans);
    138         }
    139     }
    140     return 0;
    141 }
    本文为博主原创文章,转载请注明出处。
  • 相关阅读:
    iptables 学习
    linux frp 配置
    LINUX下安装TOMCAT 及JDK方法
    更改默认源pip
    重装ORACLE参考
    pandas入门学习
    python笔记
    redis 笔记
    STM32工程编译后TIM1时钟变慢的解决
    IAR工程编译错误问题
  • 原文地址:https://www.cnblogs.com/SilverNebula/p/6481802.html
Copyright © 2020-2023  润新知