動態點分治做法:
基本上和 bzoj1095: [ZJOI2007]Hide 捉迷藏 類似的做法。
考慮每個點維護兩個 set,第一個 set 維護每個重心的子樹到它的父節點的最小距離,第二個 set 維護每個重心的每個子樹中白點到它的最小距離。查詢時先得到子樹中的答案,再暴力向上爬合併其他節點的答案。
LCT做法: 考慮 splay 每個節點維護子樹中當前節點距離最近的白點,查詢時將 和根節點打通,並旋轉到根直接查詢。虛子樹的信息用一個 multiset 維護(multiset 維護每個虛子樹裏的白點到 v 的最小距離),容易發現在 splay 上難以通過 pushup 得到想要的信息,因爲一棵 splay 維護的是一條鏈,而一個節點的左右兒子並不是這條鏈上它的直接前驅後繼。
splay 的每一個子樹都代表這條鏈連續的一段,本質上合併子樹得到根節點的信息就是在合併兩段鏈,而我們需要的是這段鏈上深度最大的點的那個答案,設 表示這段鏈上深度最低的點的答案, 表示這段鏈上深度最高的點的答案,虛子樹的信息仍用 multiset 進行維護。
虛子樹的信息只有在 access 操作時需要修改,pushup 操作就是兩段鏈的 lmn 和 rmn 進行合併(一般我寫成三段合併,根節點是隻有一個節點的鏈)
具體見代碼
動態點分治代碼:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
typedef long long ll;
#define pii pair<int,ll>
#define fir first
#define sec second
#define lowbit(i) (i & (-i))
struct Graph {
int head[maxn], to[maxn << 1], cnt, nxt[maxn << 1], w[maxn << 1];
void init() {
memset(head,-1,sizeof head);
cnt = 0;
}
void add(int u,int v,int c) {
to[cnt] = v;
nxt[cnt] = head[u];
w[cnt] = c;
head[u] = cnt++;
to[cnt] = u;
nxt[cnt] = head[v];
w[cnt] = c;
head[v] = cnt++;
}
}G;
multiset<int> val[maxn],fval[maxn];
int st[maxn * 5][30],d[maxn],cnt,a[maxn];
int fir[maxn],vis[maxn],f[maxn],root,sz[maxn],siz,p[maxn];
int n,q,RT,A;
inline int read()
{
int x=0,f=1;char ch;
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
void dfs1(int u,int fa) { //預處理深度,st表,以及歐拉序
fir[u] = ++cnt; st[cnt][0] = u;
for (int i = G.head[u]; i + 1; i = G.nxt[i]) {
int v = G.to[i];
if (v == fa) continue;
d[v] = d[u] + G.w[i];
dfs1(v,u);
st[++cnt][0] = u;
}
}
void dfs2(int u,int fa) { //求一棵子樹的重心
sz[u] = 1; f[u] = 0;
for (int i = G.head[u]; i + 1; i = G.nxt[i]) {
int v = G.to[i];
if (v == fa || vis[v]) continue;
dfs2(v,u);
sz[u] += sz[v];
if (sz[v] > f[u]) f[u] = sz[v];
}
if (siz - sz[u] > f[u]) f[u] = siz - sz[u];
if (!root || f[u] < f[root]) root = u;
}
void dfs3(int u,int fa) { //點分構建點分樹
vis[u] = 1;
int all = siz;
for (int i = G.head[u]; i + 1; i = G.nxt[i]) {
int v = G.to[i];
if (v == fa || vis[v]) continue;
root = 0, siz = sz[v];
if (siz > sz[u]) siz = all - sz[u];
dfs2(v,u);
p[root] = u; //構建點分樹
dfs3(root,u);
}
}
int cal(int u,int v) {
return d[u] < d[v] ? u : v;
}
int getlca(int x,int y) {
if (fir[x] > fir[y]) swap(x,y);
int p = log2(fir[y] - fir[x] + 1);
return cal(st[fir[x]][p],st[fir[y] - (1 << p) + 1][p]);
}
int getdis(int x,int y) {
return d[x] + d[y] - 2 * d[getlca(x,y)];
}
void prework() {
dfs1(1,0);
for (int i = 1; i <= log2(cnt); i++)
for (int j = 1; j + (1 << i) - 1 <= cnt; j++)
st[j][i] = cal(st[j][i - 1],st[j + (1 << (i - 1))][i - 1]);
root = 0, siz = n; dfs2(1,0);
RT = root; dfs3(root,0); //保留第一次的根節點,並構建點分樹
}
void add(int x) {
val[x].insert(0);
for (int i = x; p[i]; i = p[i]) {
if (fval[i].size()) {
auto t = val[p[i]].lower_bound(*fval[i].begin());
val[p[i]].erase(t);
}
fval[i].insert(getdis(x,p[i]));
val[p[i]].insert(*fval[i].begin());
}
}
void del(int x) {
val[x].erase(0);
for (int i = x; p[i]; i = p[i]) {
if (fval[i].size()) {
auto t = val[p[i]].lower_bound(*fval[i].begin());
val[p[i]].erase(t);
}
auto t = fval[i].lower_bound(getdis(x,p[i]));
fval[i].erase(t);
if (fval[i].size())
val[p[i]].insert(*fval[i].begin());
}
}
int qry(int x) {
int ans = 0x3f3f3f3f;
if (val[x].size()) ans = min(ans,*val[x].begin());
for (int i = x; p[i]; i = p[i]) {
if (fval[i].size()) {
auto t = val[p[i]].lower_bound(*fval[i].begin());
val[p[i]].erase(t);
}
if (val[p[i]].size())
ans = min(ans,*val[p[i]].begin() + getdis(x,p[i]));
if (fval[i].size())
val[p[i]].insert(*fval[i].begin());
}
return ans;
}
int main() {
n = read();
G.init();
for (int i = 1; i < n; i++) {
int u,v; u = read(); v = read();
G.add(u,v,1);
}
prework();
for (int i = 1; i <= n; i++) {
a[i] = 1;
}
q = read();
int tot = 0;
while (q--) {
int x = read(), y = read();
if (x == 1) {
if (!tot) puts("-1");
else printf("%d\n",qry(y));
} else {
a[y] ^= 1;
if (a[y]) del(y), tot--;
else add(y), tot++;
}
}
return 0;
}
LCT代碼:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e6 + 10;
typedef long long ll;
#define pii pair<int,int>
#define fir first
#define sec second
int n,m,ans;
struct node {
int u,v,a,b;
bool operator < (const node &rhs) const {
return a < rhs.a;
}
}E[maxn];
struct Graph {
int head[maxn], to[maxn], nxt[maxn], cnt;
void init() {
cnt = 0;
memset(head,-1,sizeof head);
}
void add(int u,int v) {
to[cnt] = v;
nxt[cnt] = head[u];
head[u] = cnt++;
to[cnt] = u;
nxt[cnt] = head[v];
head[v] = cnt++;
}
}G;
multiset<int> st;
inline int read(){
int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
}
struct LCT { //用splay維護原森林的連通,用到了splay的操作以及數組
#define ls ch[rt][0]
#define rs ch[rt][1]
#define inf 0x3f3f3f3f
int ch[maxn][2]; //ch[u][0] 表示 左二子,ch[u][1] 表示右兒子
int f[maxn]; //當前節點的父節點
int tag[maxn]; //翻轉標記,乘標記,加標記
int top,sta[maxn],sz[maxn],col[maxn];
multiset<int> s[maxn];
int lmn[maxn],rmn[maxn]; // splay 上每個節點對應子樹的最左端點的答案和最右端點的答案
inline bool get(int x) {
return ch[f[x]][1] == x;
}
void init() {
for (int i = 1; i <= n; i++) {
sz[i] = 1; col[i] = 0; lmn[i] = rmn[i] = inf;
}
}
int fir(int x) { // 取出第一個元素
return s[x].size() ? *s[x].begin() : inf;
}
inline void pushup(int rt) {
if (rt) {
sz[rt] = 1;
lmn[rt] = rmn[rt] = (col[rt] ? 0 : fir(rt));
if (ls) {
lmn[rt] = min(lmn[ls],sz[ls] + lmn[rt]);
rmn[rt] = min(rmn[rt],sz[rt] + rmn[ls]);
sz[rt] += sz[ls];
}
if (rs) {
lmn[rt] = min(lmn[rt],sz[rt] + lmn[rs]);
rmn[rt] = min(rmn[rs],sz[rs] + rmn[rt]);
sz[rt] += sz[rs];
}
}
}
inline bool isroot(int x) {
return (ch[f[x]][0] != x) && (ch[f[x]][1] != x);
}
inline void rotate(int x) { //旋轉操作,根據 x 在 f[x] 的哪一側進行左旋和右旋
int old = f[x], oldf = f[old];
int whichx = get(x);
if(!isroot(old)) ch[oldf][ch[oldf][1] == old] = x; //如果 old 不是根節點,就要修改 oldf 的子節點信息
ch[old][whichx] = ch[x][whichx ^ 1];
ch[x][whichx ^ 1] = old;
f[ch[old][whichx]] = old;
f[old] = x; f[x] = oldf;
pushup(old); pushup(x);
}
inline void splay(int x) { //將 x 旋到所在 splay 的根
for(int fa = f[x]; !isroot(x); rotate(x), fa = f[x]) { //再把x翻上來
if(!isroot(fa)) //如果fa非根,且x 和 fa是同一側,那麼先翻轉fa,否則先翻轉x
rotate((get(x) == get(fa)) ? fa : x);
}
}
inline void access(int x) { //access操作將x 到 根路徑上的邊修改爲重邊
int lst = 0;
while(x > 0) {
splay(x);
if (lst) {
auto tp = s[x].lower_bound(lmn[lst] + 1);
s[x].erase(tp);
}
if (ch[x][1]) s[x].insert(lmn[ch[x][1]] + 1);
ch[x][1] = lst;
pushup(x);
lst = x; x = f[x];
}
}
}tree;
void dfs(int u,int fa) {
for (int i = G.head[u]; i + 1; i = G.nxt[i]) {
int v = G.to[i];
if (v == fa) continue;
dfs(v,u);
tree.f[v] = u; tree.s[u].insert(tree.lmn[v] + 1);
tree.pushup(u);
}
}
int id,x,y,u,v,op;
int main() {
n = read();
tree.init();
G.init();
for (int i = 1; i < n; i++) {
u = read(); v = read();
G.add(u,v);
}
dfs(1,0);
m = read();
while (m--) {
op = read(); v = read();
if (op == 0) {
tree.access(v); tree.splay(v);
tree.col[v] ^= 1; tree.pushup(v);
} else {
tree.access(v); tree.splay(v);
printf("%d\n",tree.rmn[v] == inf ? -1 : tree.rmn[v]);
}
}
return 0;
}