簡要題意:給出一個n+1個點的樹,以及若干個點對,需要斷開一些點,使得這些點對路徑不連通。輸出應該斷開的最少點數。
我們斷開一個點,能夠影響到的是:
1.子樹中過這個點的路徑.
2.一個點在子樹中,另一個點在祖先中的路徑。
為了使得以上兩個影響盡可能的大,我們每次需要使得斷開的點的子樹盡可能大。
因此當我們打算斷開一對點對(u,v)的時候,為了使得斷開的點的影響盡可能大,我們需要斷開路徑上深度最小的那個點,也就是lca(u,v)。
①. 如果一對點對(u,v)的其中一個點在某個被斷開的點的子樹中,而另一個點不在,則說明這兩點間的路徑經過被斷開點,那么這條路徑就沒有再次斷開的必要了。
②. 如果一對點對兩個點都不在任何已經被斷開的點的子樹中,則說明這兩個點的lca還需要被斷開。
事實上還有一種情況, 如果兩個點都被覆蓋了,則可能出現的情況是,兩個點之間的路徑都被覆蓋了,但是卻沒有一個點被斷開。對于這種情況我們也需要將兩點間的lca斷開。但是我們斷開這次lca之后,有可能會導致之前我們處理過的,本該被判定為情況①,因為處理順序的緣故被判定為情況②,從而導致結果偏大。因此為了避免這種情況,需要離線詢問,按lca的深度從大到小來處理。
判定一個點是否在某個被斷開的點的子樹中,只需要看這個點是否被標記過。
每次斷開一個點,都需要把這個點的子樹里的點都標記上。對于這種對子樹的簡單操作,一般我們都使用dfs序,更麻煩一些的,使用樹鏈剖分。
使用了dfs序之后,給子樹標記可以使用線段樹。不過我們只需要知道一個點是否被標記過,因此使用樹狀數組即可。
#include <bits/stdc++.h>using namespace std;
const int maxn =
110000;
int p[maxn][
20], deep[maxn], L[maxn], R[maxn], cid;
vector<int> G[maxn];
void dfs(
int u,
int fa) {p[u][
0] = fa;L[u] = ++cid;deep[u] = deep[fa] +
1;
for(
auto v : G[u]) {
if(v == fa)
continue;dfs(v, u);}R[u] = cid;
}
int goup(
int x,
int len) {
for(
int i =
0; i <
20; i++) {
if(len & (
1 << i) && x)x = p[x][i];}
return x;
}
int lca(
int u,
int v) {
if(deep[u] < deep[v]) swap(u, v);
int d = deep[u] - deep[v];u = goup(u, d);
if(v == u)
return u;
for(
int i =
19; i >=
0; i--) {
if(p[u][i] == p[v][i])
continue;u = p[u][i];v = p[v][i];}
return p[u][
0];
}
namespace BIT {
int C[maxn];
void init() {
memset(C,
0,
sizeof C);}
int lowbit(
int x) {
return x & -x;}
void ins(
int x,
int v) {
for(
int i = x; i < maxn; i+=lowbit(i))C[i] += v;}
int ask(
int x) {
int res =
0;
for(
int i = x; i; i-=lowbit(i))res += C[i];
return res;}
}
struct A {
int u, v, lc, lcdep;
bool operator < (
const A & b)
const {
return lcdep > b.lcdep;}
};
void init() {
for(
int i =
0; i < maxn; i++) G[i].clear();deep[
0] =
0;cid =
0;
}
int main() {
int n;
while(~
scanf(
"%d", &n)) {init();
for(
int i =
1; i <= n; i++) {
int x, y;
scanf(
"%d%d", &x, &y);x ++, y++;G[x].push_back(y);G[y].push_back(x);}n++;dfs(
1,
0);
for(
int i =
1; i <
20; i++) {
for(
int j =
1; j <= n; j++) {
if(p[j][i-
1] ==
0) p[j][i] =
0;
else p[j][i] = p[p[j][i-
1]][i-
1];}}
int q;
scanf(
"%d", &q);
vector<A> V;
while(q--) {
int x, y;
scanf(
"%d%d", &x, &y);x++, y++;
int lc = lca(x, y);V.push_back({x, y, lc, deep[lc]});}sort(V.begin(), V.end());
int ans =
0;BIT::init();
for(
int i =
0; i < V.size(); i++) {
int u = V[i].u, v = V[i].v, lc = V[i].lc;
if(BIT::ask(L[u]) || BIT::ask(L[v]))
continue;BIT::ins(L[lc],
1);BIT::ins(R[lc] +
1, -
1); ans++;}
printf(
"%d\n", ans);}
return 0;
}
總結
以上是生活随笔為你收集整理的HDU 6203 贪心 + LCA + dfs序 + BIT的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。