fill
10.5
思路:
题意是给你一些树上路径,问最少需要多少个树上的点,把所有路径覆盖掉
40分,序列上的问题是个经典问题,区间的最小点覆盖,按右端点排序后贪心即可。
考虑推到树上后怎么做,我们也是贪心,按照路径两个端点的lca的深度从大到小排序,然后贪心的选(即如果这个路径包含了被选择的点,跳过这条路径,否则选择这条路径的lca)。
证明:如果我们lca深度最大的那条路径上选择的点不是其lca,我们不选那个点,选择lca一定不劣。然后把lca覆盖的路径删掉,这样就变成了一个子问题,证明完毕。
考虑怎么实现,如果我们选了u,v这条路径,我们只需要把lca(u,v)对应的子树那个区间都+1,判断某个区间是否被某个点覆盖只需要看u和v是否有点对应位置>0(以上都是在dfs序上操作),因为只需要实现区间加,单点查询,故树状数组即可。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 100010;
const int P = 16;
struct Path {
int u, v, lca;
Path(){}
Path(int u, int v, int lca):u(u),v(v),lca(lca){}
};
struct Edge{
int to, nxt;
}ed[N << 1];
int n, m, timx=0, idc=0;
int in[N], out[N], dep[N], anc[N][P + 1], head[N];
vector<Path> paths;
int bit[N];
void adde(int u, int v){
ed[++idc].to = v;
ed[idc].nxt = head[u];
head[u] = idc;
}
bool operator<(const Path &a, const Path &b) {
return dep[a.lca] > dep[b.lca];
}
void init() {
paths.clear();
memset(ed, 0, sizeof(ed));
memset(head, 0, sizeof(head));
for(int i=1; i<=n; i++) {
bit[i] = 0;
}
timx = 0; idc = 0;
}
void modify(int pos, int val) {
for(int i=pos; i<=n; i+=(i&-i))
bit[i] += val;
}
void modify(int lf, int rg, int val) {
modify(lf, val);
modify(rg+1, -val);
}
int query(int pos) {
int rt = 0;
for(int i = pos; i; i-=(i&-i))
rt += bit[i];
return rt;
}
void dfs(int u, int fa) {
in[u] = ++timx;
for(int p=1; p<=P; p++)
anc[u][p] = anc[anc[u][p-1]][p-1];
for(int k=head[u]; k; k=ed[k].nxt) {
int v = ed[k].to;
if(v == fa) continue;
dep[v] = dep[u] + 1;
anc[v][0] = u;
dfs(v, u);
}
out[u] = timx;
}
int LCA(int u, int v) {
if(dep[u] < dep[v]) swap(u,v);
int t = dep[u] - dep[v];
for(int p=0; t; t>>=1, p++)
if(t & 1) u = anc[u][p];
if(u == v) return u;
for(int p=P; anc[u][0]!=anc[v][0]; p--)
if(anc[u][p] != anc[v][p]) {
u = anc[u][p];
v = anc[v][p];
}
return anc[u][0];
}
int main() {
freopen ("fill.in", "r", stdin);
freopen ("fill.out", "w", stdout);
int T; scanf("%d", &T);
while(T--) {
scanf("%d%d", &n, &m);
init();
for(int i=1; i<n; i++) {
int u, v; scanf("%d%d", &u, &v);
adde(u, v); adde(v, u);
}
anc[1][0] = 1;
dep[1] = 1;
dfs(1, 1);
for(int i=1; i<=m; i++) {
int u, v; scanf("%d%d", &u, &v);
paths.push_back(Path(u, v, LCA(u, v)));
}
sort(paths.begin(), paths.end());//按深度排序
int ans = 0;
for(int t=0; t<(int)paths.size(); t++) {
if(query(in[paths[t].u]) > 0 || query(in[paths[t].v]) > 0) continue;//与处理链有交点(处理链在lca有点)
ans++;//在lca上加点
modify(in[paths[t].lca], out[paths[t].lca], 1);//更改子树
}
printf("%d\n", ans);
}
return 0;
}