1. 程式人生 > >【BZOJ 4551】【TJOI2016】【HEOI2016】樹

【BZOJ 4551】【TJOI2016】【HEOI2016】樹

ast stream 一個 std 自己的 格式 mic pan fin

題目描述

給定一棵有根樹(根為 $1$),有以下兩種操作:
$1.$ 標記操作:對某個結點打上標記(在最開始,只有結點$1$有標記,其他結點均無標記,而且對於某個結點,可以打多次標記。)
$2.$ 詢問操作:詢問某個結點最近的一個打了標記的祖先(這個結點本身也算自己的祖先)。

輸入格式

輸入第一行兩個正整數 $N$ 和 $Q$ 分別表示節點個數和操作次數
接下來 $N-1$ 行,每行兩個正整數 $u,v$ ($1\leq$$u,v$$\leq$$n$) 表示 $u$ 到$ v$ 有一條有向邊
接下來 $Q$ 行,$“ C ”$時表示這是一個標記操作,為$“ Q ”$時表示這是一個詢問操作。

輸出格式

對於每一個詢問操作,輸出一個正整數,表示結果。

輸入樣例

5 5
1 2
1 3
2 4
2 5
Q 2
C 2
Q 2
Q 5
Q 3

輸出樣例

1
2
2
1

數據範圍

$1 \leq N,Q \leq 10^5$

題解

初始時打好所有標記,逆序處理,用並查集維護,當遇到一個詢問操作時,把標記 $-1$ ,若此時標記變為 $0$ ,則將該點與父親節點合並。

技術分享圖片
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#define
N 1500005 #define depth 32 using namespace std; int n,q,tot; struct hh {int to,next;}e[N<<1]; int fa[N],dep[N],col[N],last[N],f[N],opt[N],x[N],ans[N]; void add(int a,int b) { e[++tot].to=b; e[tot].next=last[a]; last[a]=tot; } void dfs(int now) { int i; for(i=last[now];i;i=e[i].next)
if(!dep[e[i].to]) { dep[e[i].to]=dep[now]+1; fa[e[i].to]=now; dfs(e[i].to); } } int read() { int ret=0;char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)){ ret=(ret<<1)+(ret<<3)+c-0; c=getchar(); } return ret; } int find(int x){return f[x]==x?x:f[x]=find(f[x]);} int main() { int i,j,u,v,fx,fy; char flag; n=read();q=read(); for(i=1;i<=n-1;i++) { u=read();v=read(); add(u,v); add(v,u); } dfs(1); for(i=1;i<=q;i++) { scanf("\n%c",&flag); if(flag==C) opt[i]=1; else opt[i]=2; x[i]=read(); } dep[1]=1;col[1]=1; for(i=1;i<=q;i++) if(opt[i]==1) col[x[i]]++; for(i=1;i<=n;i++) f[i]=i; for(i=2;i<=n;i++) if(!col[i]) f[find(i)]=find(fa[i]); for(i=q;i>=1;i--) if(opt[i]==2) ans[++ans[0]]=find(x[i]); else { col[x[i]]--; if(!col[x[i]]) f[find(x[i])]=find(fa[x[i]]); } for(i=ans[0];i>=1;i--) printf("%d\n",ans[i]); return 0; }
View Code

【BZOJ 4551】【TJOI2016】【HEOI2016】樹