2021牛客暑期多校訓練營7 F-xay loves trees
沒有傳送門。
題面大意:給你兩棵樹,根都是1號節點。讓你找一個最大的節點集合,滿足:
1.在第一棵樹上,集合中的節點是相連的,且任意連個節點之間是祖孫關係。
2.在第二棵樹上,任意兩個節點直接都不是祖孫關係。
這題當時想出來了,結果有一個數組忘清空,先WA後RE,到最後也沒查出來。
首先根據第一個條件,這些點一定某一個節點到根的鏈的一段。那麼一種思路是處理出每一個節點\(u\)往上最高能往上走幾步(即\(ans[u]\)),最後取最大值。而且對於一條邊\((u,v)\)(\(u\)是\(v\)的父親),必有\(ans[v] \leqslant ans[u] + 1\).
那對於一個節點,如何求出\(ans[u]\)
第一種做法:
答案顯然具有單調性,因此我們對於每一個節點,二分其能向上走的步數。那麼對於一個二分值\(mid\),應該如何檢驗?即怎麼判斷這些點互相不是祖孫關係。其實就是將第二棵樹的dfs序求出來,那麼每一個點的dfs序區間就是\([dfn[u], dfn[u] + siz[u] - 1]\),只要判斷這些區間不相交即可。
如果是到根的路徑,可以用線段樹實現區間修改和查詢區間和是否為\(0\);現在改成了一段,那麼就能想到用主席樹。雖然主席樹不支援區間修改,但是可以這麼解決:建立兩棵主席樹,一棵叫“祖先樹”,一棵叫“兒子樹”。
祖先樹專門用來判斷\(u\)是否為某些點的祖先,那麼只要查詢\([dfn[u], dfn[u] +siz[u] - 1]\)
兒子樹專門用來判斷\(u\)是否為某些點的兒子,如果單點查詢\(dfn[u]\)的值是否為\(0\),那修改就要區間修改。因此我們修改的時候用差分,查詢的時候查詢字首和就行了!
這樣兩棵主席樹都是區間(字首)查詢和單點修改,主席樹就能勝任了。時間複雜度\(O(nlog^2n)\),能過。(比賽的時候是倍增的陣列忘清空了……)
第二種做法:
上面提到主席樹無法區間修改,是因為標記無法下傳(會下傳到被繼承的原來的節點)。但是如果用“標記永久化”就可以實現了。
標記永久化好久沒寫,完全忘掉了。其思路就是將改變的值的貢獻直接算到區間裡,在遞迴邊界時打標記。但是標記不下傳,而是在查詢的時候,算上標記的貢獻。
對於這道題,我們可以用標記永久化維護覆蓋某一個區間的最深的點的深度,那麼這個深度之下必然沒有點可以覆蓋這一dfs序的區間。因此我們建立一棵樹,查詢的時候就查詢該dfs序區間被覆蓋的最大深度,修改的時候就進行區間修改,更新最大值。
而且該演算法不用二分,因為我們需要的就是字首查詢,標記永久化剛好可以勝任。這樣時間複雜度就是\(O(nlogn)\)了。
一下給出兩份程式碼:
第一種做法:
#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;
}
比較長,還慢,還是看第二種做法吧。
#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;
}