没有传送门。
题面大意:给你两棵树,根都是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; }