题解 [AHOI2022] 回忆
有空去做一下 GLR 2 的手势密码
考虑贪心
对每个子树维护向上延伸的路径
那么路径分三类:对应的 \(s_i\) 还未到过的,已经到过其对应的 \(s_i\) 的,两端点为祖先关系的
还有一种是端点在某个点的两个不同子树的
称两端点为祖先关系的为一类路径,端点在某个点的两个不同子树的为二类路径
那么发现二类路径越多越好,考虑对每个子树贪心合并
具体的,有一类路径就合并,要不然拆二类路径匹配一类路径
然后考虑每个点向上的路径就好了(逃
- 貌似处理树上路径覆盖一类问题的常见方法是对每个子树维护向上和内部的路径贪心匹配?
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 200010
#define fir first
#define sec second
#define pb push_back
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, m;
vector<int> to[N];
pair<int, int> sta[N];
namespace task1{
int siz[N], f[N], lim;
void dfs(int u, int fa) {
int son=0;
for (auto& v:to[u]) if (v!=fa) {
dfs(v, u);
if (siz[v]) siz[u]=0;
son+=siz[v];
}
siz[u]+=son;
}
void solve() {
lim=0;
for (int i=1; i<=n; ++i) siz[i]=f[i]=0;
for (int i=1; i<=m; ++i) siz[sta[i].sec]=f[sta[i].sec]=1;
dfs(1, 0);
int msiz=0;
for (auto& v:to[1]) msiz=max(msiz, siz[v]), lim+=siz[v];
if (msiz<=(lim+1)/2) printf("%d\n", (lim+1)/2);
else printf("%d\n", (lim-msiz)+(2*msiz-lim));
}
}
namespace task{
int duty[N], f[N], g[N], dep[N], ds[N], top[N];
void dfs1(int u, int fa) {for (auto v:to[u]) if (v!=fa) dep[v]=dep[u]+1, dfs1(v, u);}
void merge(int u, int v) {
if (f[u]<f[v]) swap(f[u], f[v]), swap(g[u], g[v]);
f[u]-=f[v]; g[u]+=f[v];
f[v]=min(f[u]>>1, g[v])<<1;
g[u]+=f[v]; g[v]-=f[v]>>1;
f[u]-=f[v]; f[v]-=f[v];
g[u]+=g[v];
}
void dfs2(int u, int fa) {
for (auto v:to[u]) if (v!=fa) {
dfs2(v, u);
top[u]=min(top[u], top[v]);
merge(u, v);
}
if (ds[u]<top[u]) {
if (top[u]<dep[u]) {
--duty[top[u]+1];
++duty[ds[u]+1];
top[u]=ds[u];
}
else {
if (f[u]) --f[u];
else if (g[u]) --g[u], ++f[u];
++duty[ds[u]+1];
top[u]=ds[u];
}
}
f[u]+=duty[dep[u]];
duty[dep[u]]=0;
// if (u==6) cout<<f[u]<<' '<<g[u]<<endl;
}
void solve() {
for (int i=1; i<=n; ++i) f[i]=g[i]=duty[i]=0, ds[i]=top[i]=INF;
dep[1]=1; dfs1(1, 0);
for (int i=1; i<=m; ++i) ds[sta[i].sec]=min(ds[sta[i].sec], dep[sta[i].fir]);
dfs2(1, 0);
// cout<<ds[6]<<endl;
printf("%d\n", f[1]+g[1]);
}
}
signed main()
{
freopen("memory.in", "r", stdin);
freopen("memory.out", "w", stdout);
int T=read();
while (T--) {
n=read(); m=read();
for (int i=1; i<=n; ++i) to[i].clear();
for (int i=1,u,v; i<n; ++i) {
u=read(); v=read();
to[u].pb(v); to[v].pb(u);
}
for (int i=1; i<=m; ++i) sta[i].fir=read(), sta[i].sec=read();
// task1::solve();
task::solve();
}
return 0;
}