Luogu P3363 Cool loves jiaoyi 题解

Link

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;
}
posted @ 2021-10-16 11:04  Acestar  阅读(37)  评论(0编辑  收藏  举报