点分治 nbut1654 Ancient battle tree

本文讨论了使用点分治策略解决树形结构中特定条件下的点对数量问题,涉及树的最长链长计算及应用。对于树形结构问题初学者来说,此文章提供了一种有效的解题思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门:点击打开链接

题意:一棵树,求点对数,满足两点之间的最短距离等于树的最长链长。

思路:比较蠢,只想到点分治。

先用DFS求出树的最长链,然后跑点分治,这题很类似楼教主的男人八题的那题,不过那个更复杂些。

还有一个trick,就是如果n=1,只有1个点,那么最长链长等于0,满足的题意的点对有(0,0),所以应该输出1,这里需要特判一下

#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<cstdio>
#include<cctype>
#include<string>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
#define fuck(x) cout<<"["<<x<<"]"
#define FIN freopen("input.txt","r",stdin)
#define FOUT freopen("output.txt","w+",stdout)
using namespace std;
typedef long long LL;

const int MX = 4e4 + 5;
const int MS = 4e4 + 5;
const int INF = 0x3f3f3f3f;

struct Edge {
    int v, nxt, cost;
} E[MS];
int rear, Head[MX];
void edge_init() {
    rear = 0;
    memset(Head, -1, sizeof(Head));
}
void edge_add(int u, int v, int cost) {
    E[rear].v = v;
    E[rear].cost = cost;
    E[rear].nxt = Head[u];
    Head[u] = rear++;
}

int vis[MX], A[MX], cnt[MX];
int Tree_cnt(int u, int f) {
    int ret = 1;
    for(int i = Head[u]; ~i; i = E[i].nxt) {
        int v = E[i].v;
        if(v == f || vis[v]) continue;
        ret += Tree_cnt(v, u);
    }
    return ret;
}

int G_DFS(int n, int u, int f, int &ansn, int &ansid) {
    A[u] = 1; int ret = 0;
    for(int i = Head[u]; ~i; i = E[i].nxt) {
        int v = E[i].v;
        if(v == f || vis[v]) continue;
        int nxt = G_DFS(n, v, u, ansn, ansid);
        ret = max(ret, nxt); A[u] += ret;
    }
    ret = max(ret, n - A[u]);
    if(ret < ansn) ansn = ret, ansid = u;
    return A[u];
}

int Tree_G(int u) {
    int cnt = Tree_cnt(u, -1), ansn = INF, ansid = 0;
    G_DFS(cnt, u, -1, ansn, ansid);
    return ansid;
}

void Tree_deep(int u, int f, int d, int &DFN) {
    A[++DFN] = d;
    for(int i = Head[u]; ~i; i = E[i].nxt) {
        int v = E[i].v, cost = E[i].cost;
        if(v == f || vis[v]) continue;
        Tree_deep(v, u, d + cost, DFN);
    }
}

LL F_deal(int u, int k) {
    if(k <= 0) return 0;
    int DFN = 0; LL ans = 0;
    for(int i = Head[u]; ~i; i = E[i].nxt) {
        int v = E[i].v, cost = E[i].cost;
        if(vis[v]) continue;
        Tree_deep(v, u, cost, DFN);
    }
    for(int i = 1; i <= DFN; i++) cnt[i] = 0;
    for(int i = 1; i <= DFN; i++) cnt[A[i]]++;
    if(k <= DFN) ans += cnt[k];

    for(int i = 1; i < (k + 1) / 2; i++) {
        if(i <= DFN && k - i <= DFN) ans += (LL)cnt[i] * cnt[k - i];
    }
    if(k % 2 == 0 && k / 2 <= DFN) ans += (LL)cnt[k / 2] * (cnt[k / 2] - 1) / 2;
    return ans;
}

LL solve(int u, int k) {
    int g = Tree_G(u);
    vis[g] = 1;

    LL ans = F_deal(g, k);
    for(int i = Head[g]; ~i; i = E[i].nxt) {
        int v = E[i].v, cost = E[i].cost;
        if(vis[v]) continue;

        ans -= F_deal(v, k - 2 * cost);
        ans += solve(v, k);
    }
    return ans;
}

int solve_line(int u, int from, int &ans) {
    int Max1 = 0, Max2 = 0;
    for(int id = Head[u]; ~id; id = E[id].nxt) {
        int v = E[id].v;
        if(v == from) continue;

        int t = solve_line(v, u, ans) + 1;

        if(t > Max1) {
            Max2 = Max1;
            Max1 = t;
        } else if(t > Max2) Max2 = t;
    }

    ans = max(ans, Max1 + Max2);
    return Max1;
}

int main() {
    int n;
    while(~scanf("%d", &n)) {
        edge_init();
        memset(vis, 0, sizeof(vis));
        if(n == 1) {
            printf("1\n");
            continue;
        }

        for(int i = 1; i <= n - 1; i++) {
            int u, v, cost;
            scanf("%d%d", &u, &v);
            edge_add(u, v, 1);
            edge_add(v, u, 1);
        }
        int line = 0; solve_line(1, -1, line);
        printf("%I64d\n", solve(1, line));
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值