【BZOJ4551】树

题面

在2016年,佳媛姐姐刚刚学习了树,非常开心。现在他想解决这样一个问题:给定一颗有根树(根为1),有以下两种操作:

  1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均无标记,而且对于某个结点,可以打多次标记。)

  2. 询问操作:询问某个结点最近的一个打了标记的祖先(这个结点本身也算自己的祖先)

你能帮帮他吗?

30%的数据,1 ≤ N, Q ≤ 1000

70%的数据,1 ≤ N, Q ≤ 10000

100%的数据,1 ≤ N, Q ≤ 100000

分析

法一:树剖,线段树维护最右端的结点。

法二:并查集,离线倒着做,相当于清除标记。当此点被标记后,显然最近的点是自己。将它与父亲的联系切断。把自己记为父亲

如果标记被完全清除(因为可能重复标记),就和它父亲连在一起,说明这一段都是空的,可以直接忽略跳过。

查询答案就查询祖先

代码

  1. #include<iostream>  
  2. #include<cstdio>  
  3. #define N 100010  
  4. using namespace std;  
  5. int f[N],fa[N],v[N],ans[N],first[N],x,y,n,m,cnt;  
  6. struct email  
  7. {  
  8.     int u,v;  
  9.     int nxt;  
  10. }e[N*4];  
  11. struct use{int x,kind;}q[N];  
  12. char ch[5];  
  13. void add(int u,int v)  
  14. {  
  15.   e[++cnt].nxt=first[u];first[u]=cnt;  
  16.   e[cnt].u=u;e[cnt].v=v;  
  17. }  
  18. int find(int x){return x==f[x]?x:f[x]=find(f[x]);}  
  19. void dfs(int u)  
  20. {  
  21.     if(v[u]) f[u]=u;  
  22.     else f[u]=fa[u];  
  23.     for(int i=first[u];i;i=e[i].nxt)  
  24.     {  
  25.         int v=e[i].v;  
  26.         if (v!=fa[u]){fa[v]=u;dfs(v);}  
  27.     }      
  28. }  
  29. int main(){  
  30.     scanf("%d%d",&n,&m);  
  31.     for (int i=1;i<n;i++)  
  32.     {  
  33.         scanf("%d%d",&x,&y);  
  34.         add(x,y);add(y,x);  
  35.     }   
  36.     for(int i=1;i<=m;i++)  
  37.     {  
  38.         scanf("%s%d",&ch,&q[i].x);  
  39.         if (ch[0]=='C'){v[q[i].x]++;q[i].kind=1;}  
  40.     }  
  41.     v[1]++;dfs(1);  
  42.     for (int i=m;i>=1;i--)  
  43.     {  
  44.         if (q[i].kind)  
  45.         {  
  46.             v[q[i].x]--;  
  47.             if (v[q[i].x]==0)f[q[i].x]=fa[q[i].x];  
  48.         }  
  49.         else ans[i]=find(q[i].x);  
  50.     }  
  51.     for(int i=1;i<=m;i++)if(!q[i].kind)printf("%d ",ans[i]);  
  52. }  
原文地址:https://www.cnblogs.com/NSD-email0820/p/9833320.html