Luogu P3363 Cool loves jiaoyi 题解
Description
给定一棵 \(n\) 个点的树,输入 \(m\) 条路径 \(s_i, t_i\),在 \(m\) 条路径中选出若干条,使得存在一个点被经过至少 \(k\) 次,最小化路径中长度的最大值减最小值的差。
路径长度定义为经过的点数。
Solution
因为在选出的路径中只有最大值和最小值对答案有影响,所以可以先按照长度排序,然后枚举最大值和最小值,并将中间的全部选上。
对于每个 \(l,r\) ,可以用树剖将中间每条路径上的点都 \(+1\),查询最大值是否 \(\ge k\) 即可。
复杂度是 \(O(m^2\log^2 n)\) 的
考虑如何优化。
枚举两个端点过于浪费,因为中间会有很多重复的,因此可以用双指针,对于每个 \(l\) 向后找到第一个可以使树上的最大值 \(\ge k\) 的 \(r\),然后统计答案,再把 \(l\) 向后移。
复杂度 \(O(m\log^2n)\)
Code
##include <bits/stdc++.h>
##define swap(a, b) a ^= b ^= a ^= b
using namespace std;
template <typename T>
void read(T &x)
{
x = 0; char c = getchar();
while(!isdigit(c)) c = getchar();
while(isdigit(c)) x = x * 10 + c - '0', c = getchar();
return;
}
const int N = 1e5 + 5;
int n, m, k;
vector <int> g[N];
struct node
{
int s, t, val;
friend bool operator < (node x, node y)
{
return x.val < y.val;
}
}q[N];
int fa[N], dep[N], siz[N], son[N];
void dfs1(int u, int f)
{
fa[u] = f, dep[u] = dep[f] + 1, siz[u] = 1;
for(int i = 0; i < (int)g[u].size(); i++)
{
int v = g[u][i];
if(v == f) continue;
dfs1(v, u);
siz[u] += siz[v];
if(siz[v] > siz[son[u]]) son[u] = v;
}
return;
}
int top[N], id[N], cnt;
void dfs2(int u, int tp)
{
top[u] = tp, id[u] = ++cnt;
if(son[u]) dfs2(son[u], tp);
for(int i = 0; i < (int)g[u].size(); i++)
{
int v = g[u][i];
if(v == fa[u] || v == son[u]) continue;
dfs2(v, v);
}
return;
}
int lca(int x, int y)
{
while(top[x] != top[y])
{
if(dep[top[x]] < dep[top[y]]) swap(x, y);
x = fa[top[x]];
}
if(dep[x] > dep[y]) swap(x, y);
return x;
}
##define ls rt << 1
##define rs rt << 1 | 1
int mx[N << 2], tag[N << 2];
void pushup(int rt)
{
mx[rt] = max(mx[ls], mx[rs]);
}
void pushdown(int rt)
{
if(tag[rt])
{
mx[ls] += tag[rt];
mx[rs] += tag[rt];
tag[ls] += tag[rt];
tag[rs] += tag[rt];
tag[rt] = 0;
}
}
void upd(int L, int R, int v, int l, int r, int rt)
{
if(l > R || r < L) return;
if(L <= l && r <= R)
{
mx[rt] += v;
tag[rt] += v;
return;
}
pushdown(rt);
int mid = (l + r) >> 1;
upd(L, R, v, l, mid, ls);
upd(L, R, v, mid + 1, r, rs);
pushup(rt);
return;
}
int qry(int L, int R, int l, int r, int rt)
{
if(l > R || r < L) return 0;
if(L <= l && r <= R) return mx[rt];
pushdown(rt);
int mid = (l + r) >> 1;
return max(qry(L, R, l, mid, ls), qry(L, R, mid + 1, r, rs));
}
void update(int x, int y, int val)
{
while(top[x] != top[y])
{
if(dep[top[x]] < dep[top[y]]) swap(x, y);
upd(id[top[x]], id[x], val, 1, n, 1);
x = fa[top[x]];
}
if(dep[x] > dep[y]) swap(x, y);
upd(id[x], id[y], val, 1, n, 1);
return;
}
int min(int x, int y)
{
if(x == -1) return y;
return x > y ? y : x;
}
int main()
{
read(n), read(m), read(k);
for(int i = 1, u, v; i < n; i++)
{
read(u), read(v);
g[u].push_back(v);
g[v].push_back(u);
}
dfs1(1, 0), dfs2(1, 1);
for(int i = 1; i <= m; i++)
{
read(q[i].s), read(q[i].t);
q[i].val = dep[q[i].s] + dep[q[i].t] - dep[lca(q[i].s, q[i].t)] * 2 + 1;
}
sort(q + 1, q + 1 + m);
int l = 1, r = 1, ans = -1, now;
while(l <= m)
{
now = qry(1, n, 1, n, 1);
while(r <= m)
{
if(now >= k) break;
update(q[r].s, q[r].t, 1), r++;
now = qry(1, n, 1, n, 1);
}
if(now < k) break;
ans = min(ans, q[r - 1].val - q[l].val);
update(q[l].s, q[l].t, -1), l++;
}
printf("%d\n", ans);
return 0;
}
$$A\ drop\ of\ tear\ blurs\ memories\ of\ the\ past.$$