Acwing264.权值 (点分治)

image

考虑怎么处理跨根的链:

  • 因为这题是求最值,不用考虑容斥,直接小合并(每次把一个小的子树合到已知的里面)。
  • 当前子树和之前子树的集合合并:之前子树集合维护一个hash[i],到重心距离为i的边数最小值是hash[i]。
  • 处理出当前子树所有点到重心的距离fi和经过的边se,如果fi == k 直接用se更新,否则找对应hash,hash[k - fi]中用他们的和更新。
  • 计算距离时nowdis 大于k 直接return,既剪枝时间, 又使hash空间大小得到保证。
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false) ,cin.tie(0), cout.tie(0);
//#pragma GCC optimize(3,"Ofast","inline")
#define ll long long
#define PII pair<int, int>
const int N = 2e5 + 5;
const int M = 1e6 + 5;
const int INF = 0x3f3f3f3f;
const ll LNF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double PI = acos(-1.0);
int h[N], e[N << 1], w[N << 1], ne[N << 1], idx;
int gravity_max[N], gravity_size[N], gravity;
int sz; bool div_vis[N];
PII dis[N];int distot; int f[M]; int Clear[N], cnt_Clear;
int n, k; int ans = 0x3f3f3f3f;
void add ( int a, int b, int c ) {
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}
void get_gravity ( int u, int fa ) { //得到重心
    gravity_size[u] = 1; gravity_max[u] = -1;
    for ( int i = h[u]; ~i; i = ne[i] ) {
        int v = e[i]; if(v == fa || div_vis[v]) continue;
        get_gravity(v, u);
        if(gravity_max[u] == -1 || gravity_max[u] < gravity_size[v]) {
            gravity_max[u] = gravity_size[v];
        }
        gravity_size[u] += gravity_size[v];
    }
    gravity_max[u] = max(gravity_max[u], sz - gravity_size[u]);
    if(gravity == -1 || gravity_max[gravity] > gravity_max[u]) gravity = u;
}
void get_size (int u, int fa) { //跑当前树大小
    ++ sz;
    for ( int i = h[u]; ~i; i = ne[i] ) {
        int v = e[i];
        if ( v == fa || div_vis[v] ) continue;
        get_size(v, u);
    }
}
void get_dis(int u, int fa, ll nowdis, int cnt) { //每个点到u的距离算出来
    if( nowdis > k) return; //剪枝,否则f要爆
    dis[++ distot] = {nowdis, cnt};
    for ( int i = h[u]; ~i; i = ne[i] ) {
        int v = e[i]; if(v == fa || div_vis[v]) continue;
        get_dis(v, u, nowdis + w[i], cnt + 1);
    }
}

void tree_div ( int u ) {
    sz = 0;
    get_size(u, -1); //跑当前树大小
    gravity = - 1;
    get_gravity(u, -1); // 跑当前树重心
    div_vis[gravity] = true; //去掉根节点,所有操作不再管gra
    cnt_Clear = 0;
    for ( int i = h[gravity]; ~i; i = ne[i] ) {
        int v = e[i]; if( div_vis[v] ) continue;
        distot = 0;
        get_dis(v, gravity, w[i], 1);
        for ( int j = 1; j <= distot; ++ j ) {
            auto t = dis[j];
            if(k - t.first > 0) ans = min(ans, f[k - t.first] + t.second);
            else if(k - t.first == 0) ans = min(ans, t.second);
        }
        for ( int j = 1; j <= distot; ++ j ) {
            auto t = dis[j];
            f[t.first] = min(f[t.first], t.second);
            Clear[++ cnt_Clear] = t.first;
        }
    }
    for ( int i = 1; i <= cnt_Clear; ++ i ) {
        f[Clear[i]] = INF;
    }
    for ( int i = h[gravity]; ~i; i = ne[i] ) { // 递归处理子树信息
        int v = e[i]; if( div_vis[v] ) continue;
        tree_div(v); 
    }
}
void init() {
    memset(h, -1, sizeof h);
    memset(div_vis, 0, sizeof div_vis);
    memset(f, 0x3f, sizeof f); idx = 0;
}
int main () {
    IOS
    cin >> n >> k;
    init(); int u, v, c;
    for ( int i = 1; i < n; ++ i ) {
        cin >> u >> v >> c; ++ u, ++ v;
        add(u, v, c); add(v, u, c);
    }

    tree_div(1);

    cout << (ans == INF ? "NAY" : "AYE") << '\n';
    
    return 0;
}
posted @ 2022-04-02 16:52  qingyanng  阅读(16)  评论(0编辑  收藏  举报