P4183 [USACO18JAN]Cow at Large P

传送门


思路

我们不妨设出发点为根 \(rt\),并设 \(d[u]\) 表示 \(u\) 到最近的叶子结点的距离。

显然,如果一个点 \(u\) 满足:\(rt\)\(u\) 的距离 \(\ge\) \(d[u]\),那么我们就可以放一个农民在那个叶子,然后跑到 \(u\) 结点,这样一来 \(u\) 这棵子树都不能再通行了。

因此,我们先用 bfs 求出每个点 \(u\)\(d[u]\),然后从根出发,对于访问到的结点,如果满足上面那条式子,就对答案贡献加 1,并且不用继续向下遍历。

这样的复杂度是 \(O(n^2)\) 的。

我们考虑用点分治来优化。

我们可以发现,如果父亲可以选择,那么他们的儿子也一定能被选择,但我们并不需要儿子进行贡献。

我们可以考虑将每个点的权值设为 \(2-r[u]\),其中 \(r[u]\)\(u\) 的度数。(对于根节点,我们权值应该设为 \(1-r[u]\) 才对,但实际上影响的只有那些根节点也为叶节点的情况,因此我们特判即可。)

点分治时,我们处理出点 \(u\) 到重心的距离,记为深度 \(dep[u]\)

对于一个询问 \(u\)(即 \(u\) 为根),如果点 \(v\) 能够被选择,则需要满足 \(d[v]\le dep[u]+dep[v]\)

我们移项,得到 \(d[v]-dep[v]\le dep[u]\),记 \(sub[u]=d[u]-dep[u]\)

那么我们可以以 \(sub[u]\) 为下标,存入权值 \(2-r[u]\),然后再做一个前缀和。

然后查询 \(dep[u]\) 上的值,贡献到答案即可。

注意容斥掉同一棵子树的情况。


代码

#include<iostream>
#include<fstream>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#define LL long long
#define INF 0x7fffffff
inline int reads()
{
    int sign = 1, re = 0; char c = getchar();
    while(c < '0' || c > '9'){if(c == '-') sign = -1; c = getchar();}
    while('0' <= c && c <= '9'){re = re * 10 + (c - '0'); c = getchar();}
    return sign * re;
}
int n, ans[70005];
std::vector<int> r[70005];
int d[70005];
inline void bfs()
{
    static std::queue<int> q;
    for(int i = 1; i <= n; i++)
        if(r[i].size() == 1) q.push(i);
        else d[i] = -1;
    while(!q.empty())
    {
        int now = q.front(); q.pop();
        for(int to : r[now])
        {
            if(d[to] != -1) continue;
            d[to] = d[now] + 1;
            q.push(to);
        }
    }
            
}
int sz[70005], Mx[70005], tot, rt;
std::bitset<70005> vis;
void getrt(int now, int fa)
{
    sz[now] = 1, Mx[now] = 0;
    for(int to : r[now])
    {
        if(vis[to] || to == fa) continue;
        getrt(to, now);
        sz[now] += sz[to], Mx[now] = std::max(Mx[now], sz[to]);
    }
    Mx[now] = std::max(Mx[now], tot - sz[now]);
    if(Mx[now] < Mx[rt]) rt = now;
}
int dep[70005], sub[70005], _val[140005], *val = _val + 70000, tl, tr;
std::vector<int> q, st;
void getdis(int now, int fa)
{
    for(int to : r[now])
    {
        if(vis[to] || to == fa) continue;
        dep[to] = dep[now] + 1;
        sub[to] = d[to] - dep[to];
        q.emplace_back(to);
        getdis(to, now);
    }
}
inline int query(int x) {return x < tl ? 0 : x > tr ? val[tr] : val[x];}
inline void calc(int now)
{
    dep[now] = 0, sub[now] = d[now];
    st.emplace_back(now);
    for(int to : r[now])
    {
        if(vis[to]) continue;
        dep[to] = 1, sub[to] = d[to] - 1, q.emplace_back(to);
        getdis(to, now);
        tl = INF, tr = -INF;
        for(int i : q) tl = std::min(tl, sub[i]), tr = std::max(tr, sub[i]);
        memset(val + tl, 0, sizeof(int) * (tr - tl + 1));
        for(int i : q) val[sub[i]] += 2 - r[i].size();
        for(int i = tl + 1; i <= tr; i++) val[i] += val[i - 1];
        for(int i : q) ans[i] -= query(dep[i]);
        st.insert(st.begin(), q.begin(), q.end()), q.clear();
    }
    tl = INF, tr = -INF;
    for(int i : st) tl = std::min(tl, sub[i]), tr = std::max(tr, sub[i]);
    memset(val + tl, 0, sizeof(int) * (tr - tl + 1));
    for(int i : st) val[sub[i]] += 2 - r[i].size();
    for(int i = tl + 1; i <= tr; i++) val[i] += val[i - 1];
    for(int i : st) ans[i] += query(dep[i]);
    st.clear();
}
void solve(int now)
{
    vis[now] = 1, calc(now);
    int rp = tot - sz[now];
    for(int to : r[now])
    {
        if(vis[to]) continue;
        tot = sz[rt = 0] = (sz[to] > sz[now] ? rp : sz[now]);
        getrt(to, 0);
        solve(rt);
    }
}
signed main()
{
#ifndef ONLINE_JUDGE
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
    n = reads();
    for(int i = 1; i < n; i++)
    {
        int u = reads(), v = reads();
        r[u].emplace_back(v), r[v].emplace_back(u);
    } bfs();
    tot = Mx[0] = n;
    getrt(1, 0);
    solve(rt);
    for(int i = 1; i <= n; i++) printf("%d\n", ans[i] - (!d[i]));
    return 0;
}
posted @ 2022-06-18 09:35  zuytong  阅读(27)  评论(0编辑  收藏  举报