沒有傳送門。
題面大意:給你兩棵樹,根都是1號節點。讓你找一個最大的節點集合,知足:
1.在第一棵樹上,集合中的節點是相連的,且任意連個節點之間是祖孫關係。
2.在第二棵樹上,任意兩個節點直接都不是祖孫關係。
這題當時想出來了,結果有一個數組忘清空,先WA後RE,到最後也沒查出來。
首先根據第一個條件,這些點必定某一個節點到根的鏈的一段。那麼一種思路是處理出每個節點\(u\)往上最高能往上走幾步(即\(ans[u]\)),最後取最大值。並且對於一條邊\((u,v)\)(\(u\)是\(v\)的父親),必有\(ans[v] \leqslant ans[u] + 1\).c++
那對於一個節點,如何求出\(ans[u]\)呢?我如今會兩種作法:
第一種作法:
答案顯然具備單調性,所以咱們對於每個節點,二分其能向上走的步數。那麼對於一個二分值\(mid\),應該如何檢驗?即怎麼判斷這些點互相不是祖孫關係。其實就是將第二棵樹的dfs序求出來,那麼每個點的dfs序區間就是\([dfn[u], dfn[u] + siz[u] - 1]\),只要判斷這些區間不相交便可。git
若是是到根的路徑,能夠用線段樹實現區間修改和查詢區間和是否爲\(0\);如今改爲了一段,那麼就能想到用主席樹。雖然主席樹不支持區間修改,可是能夠這麼解決:創建兩棵主席樹,一棵叫「祖先樹」,一棵叫「兒子樹」。算法
祖先樹專門用來判斷\(u\)是否爲某些點的祖先,那麼只要查詢\([dfn[u], dfn[u] +siz[u] - 1]\)這段區間和是否爲\(0\)。而修改剛好是單點修改,將\(dfn[u]\)標記成\(1\)便可。數組
兒子樹專門用來判斷\(u\)是否爲某些點的兒子,若是單點查詢\(dfn[u]\)的值是否爲\(0\),那修改就要區間修改。所以咱們修改的時候用差分,查詢的時候查詢前綴和就好了!ide
這樣兩棵主席樹都是區間(前綴)查詢和單點修改,主席樹就能勝任了。時間複雜度\(O(nlog^2n)\),能過。(比賽的時候是倍增的數組忘清空了……)
第二種作法:
上面提到主席樹沒法區間修改,是由於標記沒法下傳(會下傳到被繼承的原來的節點)。可是若是用「標記永久化」就能夠實現了。spa
標記永久化很久沒寫,徹底忘掉了。其思路就是將改變的值的貢獻直接算到區間裏,在遞歸邊界時打標記。可是標記不下傳,而是在查詢的時候,算上標記的貢獻。繼承
對於這道題,咱們能夠用標記永久化維護覆蓋某一個區間的最深的點的深度,那麼這個深度之下必然沒有點能夠覆蓋這一dfs序的區間。所以咱們創建一棵樹,查詢的時候就查詢該dfs序區間被覆蓋的最大深度,修改的時候就進行區間修改,更新最大值。遞歸
並且該算法不用二分,由於咱們須要的就是前綴查詢,標記永久化恰好能夠勝任。這樣時間複雜度就是\(O(nlogn)\)了。
一下給出兩份代碼:
第一種作法:get
#include<bits/stdc++.h> using namespace std; #define enter puts("") #define space putchar(' ') #define Mem(a, x) memset(a, x, sizeof(a)) #define In inline #define forE(i, x, y) for(int i = head[x], y; ~i && (y = e[i].to); i = e[i].nxt) typedef long long ll; const int maxn = 3e5 + 500; const int maxt = 1.2e7 + 5; const int N = 18; ll read() {ll x; scanf("%lld", &x); return x;} void write(ll x) {printf("%lld", x);} int n; struct Edge { int nxt, to; }e[maxn << 1]; int head[maxn], ecnt = -1; In void addEdge(int x, int y) { e[++ecnt] = (Edge){head[x], y}; head[x] = ecnt; } Edge e2[maxn << 1]; int head2[maxn], ecnt2 = -1; In void addEdge2(int x, int y) { e2[++ecnt2] = (Edge){head2[x], y}; head2[x] = ecnt2; } int dfsx[maxn], siz[maxn], cnt = 0; In void dfs2(int now, int _f) { siz[now] = 1; dfsx[now] = ++cnt; for(int i = head2[now], v; ~i && (v = e2[i].to); i = e2[i].nxt) { if(v == _f) continue; dfs2(v, now); siz[now] += siz[v]; } } const int P = maxn - 20; struct Tree { int ls = 0, rs = 0, sum = 0; In void init() {ls = rs = sum = 0;} }; struct trees { Tree t[maxt]; int root[maxn << 1], cnt; In void init() {Mem(root, 0), cnt = 0;} In void insert(int old, int& now, int l, int r, int x, int d) { t[now = ++cnt] = t[old]; if(x < l || x > r) return; t[now].sum += d; if(l == r) return; int mid = (l + r) >> 1; if(x <= mid) insert(t[old].ls, t[now].ls, l, mid, x, d); else insert(t[old].rs, t[now].rs, mid + 1, r, x, d); } In int query(int old, int now, int l, int r, int L, int R) { if(!old && !now) return 0; if(l == L && r == R) return t[now].sum - t[old].sum; int mid = (l + r) >> 1; if(R <= mid) return query(t[old].ls, t[now].ls, l, mid, L, R); else if(L > mid) return query(t[old].rs, t[now].rs, mid + 1, r, L, R); else return query(t[old].ls, t[now].ls, l, mid, L, mid) + query(t[old].rs, t[now].rs, mid + 1, r, mid + 1, R); } }tA, tS; int ha[maxn], dep[maxn], fa[N + 2][maxn]; In int calc(int x, int len) { int sta = x; for(int i = ha[len]; i >= 0; --i) { int y = fa[i][x], z = fa[0][y]; if(!y) continue; int tp1 = tA.query(tA.root[z], tA.root[fa[0][sta]], 1, n, dfsx[sta], dfsx[sta] + siz[sta] - 1); int tp2 = tS.query(tS.root[z], tS.root[fa[0][sta]], 1, n, 1, dfsx[sta]); if(!tp1 && !tp2) x = y; } return dep[sta] - dep[x]; } int ans[maxn]; In void dfs1(int now, int _f) { for(int i = 1; (1 << i) <= dep[now]; ++i) fa[i][now] = fa[i - 1][fa[i - 1][now]]; ans[now] = min(ans[_f] + 1, calc(now, ans[_f] + 1)); tA.insert(tA.root[_f], tA.root[now], 1, n, dfsx[now], 1); tS.insert(tS.root[_f], tS.root[P], 1, n, dfsx[now], 1); tS.insert(tS.root[P], tS.root[now], 1, n, dfsx[now] + siz[now], -1); forE(i, now, v) { if(v == _f) continue; dep[v] = dep[now] + 1; fa[0][v] = now; dfs1(v, now); } } In void init() { Mem(fa, 0); Mem(head, -1), ecnt = -1; Mem(head2, -1), ecnt2 = -1; cnt = 0; tA.init(), tS.init(); } int main() { int T = read(); while(T--) { init(); n = read(); for(int i = 1; i < n; ++i) { int x = read(), y = read(); addEdge(x, y), addEdge(y, x); } for(int i = 1; i < n; ++i) { int x = read(), y = read(); addEdge2(x, y), addEdge2(y, x); } dfs2(1, 0); for(int i = 2; i <= n; ++i) ha[i] = ha[i >> 1] + 1; dep[1] = 1, dfs1(1, 0); int Max = 0; for(int i = 1; i <= n; ++i) Max = max(Max, ans[i]); write(Max + 1), enter; } return 0; }
比較長,還慢,仍是看第二種作法吧。it
#include<bits/stdc++.h> using namespace std; #define enter puts("") #define space putchar(' ') #define Mem(a, x) memset(a, x, sizeof(a)) #define In inline typedef long long ll; typedef double db; const int INF = 0x3f3f3f3f; const db eps = 1e-8; const int maxn = 3e5 + 5; const int maxt = 6e6 + 5; In ll read() { ll ans = 0; char ch = getchar(), las = ' '; while(!isdigit(ch)) las = ch, ch = getchar(); while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar(); if(las == '-') ans = -ans; return ans; } In void write(ll x) { if(x < 0) x = -x, putchar('-'); if(x >= 10) write(x / 10); putchar(x % 10 + '0'); } int n; #define pb push_back vector<int> v1[maxn], v2[maxn]; int dfsx[maxn], siz[maxn], cnt = 0; In void dfs2(int now, int _f) { siz[now] = 1, dfsx[now] = ++cnt; for(auto v : v2[now]) { if(v == _f) continue; dfs2(v, now); siz[now] += siz[v]; } } struct Tree { int ls, rs, lzy, Max; }t[maxt]; int root[maxn], tcnt = 0; In void insert(int old, int& now, int l, int r, int L, int R, int d) { t[now = ++tcnt] = t[old]; t[now].Max = max(t[now].Max, d); if(l == L && r == R) return (void)(t[now].lzy = max(t[now].lzy, d)); int mid = (l + r) >> 1; if(R <= mid) insert(t[old].ls, t[now].ls, l, mid, L, R, d); else if(L > mid) insert(t[old].rs, t[now].rs, mid + 1, r, L, R, d); else insert(t[old].ls, t[now].ls, l, mid, L, mid, d), insert(t[old].rs, t[now].rs, mid + 1, r, mid + 1, R, d); } In int query(int now, int l, int r, int L, int R) { if(l == L && r == R) return t[now].Max; int ret, mid = (l + r) >> 1; if(R <= mid) ret = query(t[now].ls, l, mid, L, R); else if(L > mid) ret = query(t[now].rs, mid + 1, r, L, R); else ret = max(query(t[now].ls, l, mid, L, mid), query(t[now].rs, mid + 1, r, mid + 1, R)); return max(ret, t[now].lzy); } int dep[maxn], f[maxn]; In void dfs1(int now, int _f) { f[now] = min(f[_f] + 1, dep[now] - query(root[_f], 1, n, dfsx[now], dfsx[now] + siz[now] - 1)); insert(root[_f], root[now], 1, n, dfsx[now], dfsx[now] + siz[now] - 1, dep[now]); for(auto v : v1[now]) { if(v == _f) continue; dep[v] = dep[now] + 1; dfs1(v, now); } } In void init() { for(int i = 1; i <= n; ++i) v1[i].clear(), v2[i].clear(); Mem(root, 0), cnt = tcnt = 0; } int main() { int T = read(); while(T--) { n = read(); init(); for(int i = 1, x, y; i < n; ++i) x = read(), y = read(), v1[x].pb(y), v1[y].pb(x); for(int i = 1, x, y; i < n; ++i) x = read(), y = read(), v2[x].pb(y), v2[y].pb(x); dfs2(1, 0); dep[1] = 1, dfs1(1, 0); int Max = 0; for(int i = 1; i <= n; ++i) Max = max(Max, f[i]); write(Max), enter; } return 0; }