【BZOJ4551】树
题面
在2016年,佳媛姐姐刚刚学习了树,非常开心。现在他想解决这样一个问题:给定一颗有根树(根为1),有以下两种操作:
-
标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均无标记,而且对于某个结点,可以打多次标记。)
-
询问操作:询问某个结点最近的一个打了标记的祖先(这个结点本身也算自己的祖先)
你能帮帮他吗?
30%的数据,1 ≤ N, Q ≤ 1000
70%的数据,1 ≤ N, Q ≤ 10000
100%的数据,1 ≤ N, Q ≤ 100000
分析
法一:树剖,线段树维护最右端的结点。
法二:并查集,离线倒着做,相当于清除标记。当此点被标记后,显然最近的点是自己。将它与父亲的联系切断。把自己记为父亲
如果标记被完全清除(因为可能重复标记),就和它父亲连在一起,说明这一段都是空的,可以直接忽略跳过。
查询答案就查询祖先
代码
- #include<iostream>
- #include<cstdio>
- #define N 100010
- using namespace std;
- int f[N],fa[N],v[N],ans[N],first[N],x,y,n,m,cnt;
- struct email
- {
- int u,v;
- int nxt;
- }e[N*4];
- struct use{int x,kind;}q[N];
- char ch[5];
- void add(int u,int v)
- {
- e[++cnt].nxt=first[u];first[u]=cnt;
- e[cnt].u=u;e[cnt].v=v;
- }
- int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
- void dfs(int u)
- {
- if(v[u]) f[u]=u;
- else f[u]=fa[u];
- for(int i=first[u];i;i=e[i].nxt)
- {
- int v=e[i].v;
- if (v!=fa[u]){fa[v]=u;dfs(v);}
- }
- }
- int main(){
- scanf("%d%d",&n,&m);
- for (int i=1;i<n;i++)
- {
- scanf("%d%d",&x,&y);
- add(x,y);add(y,x);
- }
- for(int i=1;i<=m;i++)
- {
- scanf("%s%d",&ch,&q[i].x);
- if (ch[0]=='C'){v[q[i].x]++;q[i].kind=1;}
- }
- v[1]++;dfs(1);
- for (int i=m;i>=1;i--)
- {
- if (q[i].kind)
- {
- v[q[i].x]--;
- if (v[q[i].x]==0)f[q[i].x]=fa[q[i].x];
- }
- else ans[i]=find(q[i].x);
- }
- for(int i=1;i<=m;i++)if(!q[i].kind)printf("%d\n",ans[i]);
- }
“Make my parents proud,and impress the girl I like.”