跳转至

250602 模拟赛 T1 题解

题意

给定一棵 \(n\) 个节点的无根树,边有边权。记 \(dis(u,v)\) 表示 \(u\)\(v\) 在树上的距离,\(f(x,u,v)\) 表示 \(u,v\) 简单路径上离 \(x\) 最近的一个点到 \(v\) 的距离。

给定常数 \(k\),求有多少有序对 \((u,v)\) 满足

\[ \frac 1n\sum_{x=1}^nf(x,u,v)\ge \frac 12 dis(u,v)+k \]

其中 \(n\le 10^6\)

题解

在固定 \(u,v\) 的情况下,记 \(y_x\) 表示 \(u,v\) 简单路径上离 \(x\) 最近的一个点。我们将题目给定的约束中不够直观、复杂的东西变形,得到:

\[ 2\sum_{x=1}^{n}dis(y_x,v)\ge n\times dis(u,v)+2nk \]

注意到 \(\sum dis(y_x,v)\) 是路径上每个节点挂的子树大小的加权和,比较容易计算;这里可以使用点分治做到 \(O(n\log^2n)\),获得 \(75pts\)

我们发现,直接通过这个式子似乎无法获得更低的时间复杂度。因此尝试继续变形:

\[ \begin{align*} \sum_{x=1}^n2dis(y_x,v)-dis(u,v)\ge&\ 2nk\\ \sum_{x=1}^ndis(y_x,v)-dis(y_x,u)\ge &\ 2nk\\ \end{align*} \]

式子的形式似乎又变好了一点,但是没有产生任何本质的变化。注意到 \(y_x\) 会让式子变得难以计算,因为它和 \(u,v,x\) 同时有关。注意到我们可以加上 \(dis(x,y_x)\) 再减去 \(dis(x,y_x)\)

\[ \begin{align*} \sum_{x=1}^n dis(x,v)-dis(x,u)\ge&\ 2nk\\ \sum_{x=1}^n dis(x,v)-\sum_{x=1}^n dis(x,u)\ge&\ 2nk \end{align*} \]

其中 \(\sum dis(x,u)\) 是只和 \(u\) 有关的常量。我们先求出所有 \(s_u=\sum dis(x,u)\),排序后双指针即可做到 \(O(n\log n)\)

AC 代码
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int N = 1e6 + 10;

struct Edge {
    int v, w, next;
} pool[2 * N];
int ne, head[N];

void addEdge(int u, int v, int w) {
    pool[++ne] = {v, w, head[u]};
    head[u] = ne;
}

int T;
int n, k;
ll ans;

void clear() {
    for(int i = 1; i <= n; i++) head[i] = 0;
    ne = 0;
    ans = 0;
}

ll sz[N], sum[N];

ll dfs1(int u, int fa) {
    ll res = 0;
    sz[u] = 1;
    for(int e = head[u]; e; e = pool[e].next) {
        int v = pool[e].v, w = pool[e].w;
        if(v == fa) continue;
        res += dfs1(v, u);
        sz[u] += sz[v];
        res += sz[v] * w;
    }
    return res;
}

void dfs2(int u, int fa) {
    for(int e = head[u]; e; e = pool[e].next) {
        int v = pool[e].v, w = pool[e].w;
        if(v == fa) continue;
        sum[v] = sum[u] + (n - 2 * sz[v]) * w;
        dfs2(v, u);
    }
}

int main() {

    cin >> T;
    while(T--) {
        cin >> n >> k;
        clear();
        for(int i = 1; i <= n - 1; i++) {
            int u, v, w;
            cin >> u >> v >> w;
            addEdge(u, v, w);
            addEdge(v, u, w);
        }
        sum[1] = dfs1(1, 0);
        dfs2(1, 0);
        sort(sum + 1, sum + 1 + n);
        for(int i = 1, j = 1; i <= n; i++) {
            while(sum[i] - sum[j + 1] >= 2ll * n * k) ++j;
            if(sum[i] - sum[j] >= 2ll * n * k) {
                ans += j;
            }
        }
        cout << ans << '\n';
    }

    return 0;
}