题干:
给出一个n+1个点的树,以及p个点对,需要断开一些点,使得这p个点对路径不连通。输出应该断开的最少点数。
解题报告:
从那p个点对入手的话:首先考虑只有一对点的话,肯定是这条路径上的随便一个点都可以。两个点对呢?肯定是有交点就选交点,没交点就只能破坏两个点了。多个点对呢?比较难考虑了。也比较难想到正解:贪心思维,破坏两个点的LCA是最佳的。
从破坏的点入手:破坏一个点,可以影响到的是所有经过他的所有的路径,转化到有根树中,不难想到这个点就是lca。
当然这个题到这里还不够,还需要加点贪心的思想,对p个点对按照他们的lca深度从大到小排序,如果某个点需要被破坏,那么它的所有子节点(子树)都可以不再需要破坏别的点了(因为它的子节点到别的子节点肯定是要经过该点的,要注意这个前提是lca是排好序的。所以对应的操作就是对子树的所有节点都+1。
为什么要排序呢?因为这样就能保证,当前处理到第i对点u,v,如果这个点所在任意子树的某祖先x如果有被标记过,那么这条路径一定就不用管了,因为uv的lca一定深度小于x,也就是说你u想到达v,一定要经过x,又因为x已经被破坏了,所以这对点直接不用管了,真神奇的想法!!
所以接下来问题就是判断现在在(u,v)之间的路径上有没有被破坏的点(也就是查询u和v这两点的值是不是>0的),如果没有的话那么此时就要破坏这个lca点,如果有的话就直接continue。
所以dfs序来维护子树,然后用树状数组差分来维护区间和,查询的时候查询单点值。(当然,这后一步可以直接用线段树来取代)
AC代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define F first
#define S second
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
typedef pair<int,int> PII;
const int MAX = 2e4 + 5;
int n,in[MAX],out[MAX],clk,dep[MAX],id[MAX],c[MAX*10];
int fa[MAX][33];
vector<int> vv[MAX];
struct Node {int a,b,lca;bool operator <(const Node & bb)const {return dep[lca] > dep[bb.lca];}
} p[MAX*5];
int lowbit(int x){ return x&(-x); }
int query(int pos) {int res = 0;while(pos > 0) {res += c[pos];pos -= lowbit(pos);}return res;
}
void update(int pos,int val) {while(pos < MAX) {c[pos] += val;pos += lowbit(pos);}
}
void dfs(int cur,int rt,int deep) {in[cur] = ++clk;dep[cur] = deep;fa[cur][0]=rt;id[clk] = cur;int up = vv[cur].size();for(int i = 1; i<=31; i++) {fa[cur][i] = fa[fa[cur][i-1]][i-1];}for(int i = 0; i<up; i++) {int v = vv[cur][i];if(v == rt) continue;dfs(v,cur,deep+1);}out[cur] = clk;
}
int lca(int u,int v) {if(dep[v] > dep[u]) swap(u,v);for(int i = 31; i>=0; i--) {if(dep[fa[u][i]] >= dep[v]) u = fa[u][i];}if(u == v) return u;for(int i = 31; i>=0; i--) {if(fa[u][i] != fa[v][i]) u = fa[u][i],v=fa[v][i];}return fa[u][0];
}
int main()
{while(~scanf("%d",&n)) {int a,b;clk=0;memset(fa,0,sizeof fa);memset(c,0,sizeof c);for(int i = 0; i<=n+1; i++) vv[i].clear();for(int i = 1; i<=n; i++) {scanf("%d%d",&a,&b);a++,b++;vv[a].pb(b);vv[b].pb(a);}dfs(1,0,1);int q;scanf("%d",&q);for(int i = 1; i<=q; i++) {scanf("%d%d",&a,&b);a++,b++;p[i].a = a,p[i].b = b,p[i].lca = lca(a,b);}sort(p+1,p+q+1);int ans = 0;for(int i = 1; i<=q; i++) {if(query(in[p[i].a]) + query(in[p[i].b]) == 0) {ans++;update(in[p[i].lca],1);update(out[p[i].lca]+1,-1);}}printf("%d\n",ans);}return 0 ;
}