码迷,mamicode.com
首页 > 其他好文 > 详细

[Comet OJ - Contest #6 C][48C 2279]一道树题_树

时间:2019-07-24 22:55:26      阅读:208      评论:0      收藏:0      [点我收藏+]

标签:typedef   names   复杂   str   alc   端点   char   之间   cpp   

一道树题

题目大意

给定一棵树,边的编号为读入顺序。现在规定,区间$[L, R]$的贡献$S(L,R)$为把编号在该区间里的边都连上后,当前形成的森林中点数大于等于$2$的联通块个数。

求$\sum\limits_{i = 1} ^ {N - 1}\sum\limits_{j = i} ^ {N - 1}S(i,j)$。

数据范围:$2\le N\le 10^5$。


题解

水题。

我们发现,一棵树上假设联通了$k$条边,那么联通块个数就是$N-k$个。所以我们可以求出,所有区间下的所有联通块个数和。

现在我们要减掉,每个区间中形成的点联通块。

这个好办。

假设以这个点$p$为端点的边的编号从小到大一次为$a_1$一直到$a_m$。

那么如果一个区间满足这个区间不跨过任意一个$a$即可,这个就是两个$a$之间求一个区间个数。

由于我们需要把每个点的$a$数组排序,所以复杂度是$O(nlogn)$。

代码

#include <bits/stdc++.h>

#define N 200010 

using namespace std;

typedef long long ll;

int head[N], to[N << 1], nxt[N << 1], val[N << 1], tot, n;

ll ans;

char *p1, *p2, buf[100000];

#define nc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1 ++ )

int rd() {
    int x = 0, f = 1;
    char c = nc();
    while (c < 48) {
        if (c == ‘-‘)
            f = -1;
        c = nc();
    }
    while (c > 47) {
        x = (((x << 2) + x) << 1) + (c ^ 48), c = nc();
    }
    return x * f;
}

inline void add(int x, int y, int z) {
    to[ ++ tot] = y;
    val[tot] = z;
    nxt[tot] = head[x];
    head[x] = tot;
}

int a[N];

inline ll calc(int l, int r) {
    // printf("%d %d\n", l, r);
    if (l > r) {
        return 0;
    }
    ll len = r - l + 1;
    return len * (len + 1) / 2;
}

void dfs(int p, int fa) {
    // cout << p << endl ;
    int cnt = 0;
    for (int i = head[p]; i; i = nxt[i]) {
        a[ ++ cnt] = val[i];
    }
    sort(a + 1, a + cnt + 1);
    a[0] = 0;
    for (int i = 1; i <= cnt; i ++ ) {
        ans -= calc(a[i - 1] + 1, a[i] - 1);
    }
    ans -= calc(a[cnt] + 1, n - 1);
    for (int i = head[p]; i; i = nxt[i]) {
        if (to[i] != fa) {
            dfs(to[i], p);
        }
    }
}

int main() {
    n = rd();
    for (int i = 1; i < n; i ++ ) {
        int x = rd(), y = rd();
        add(x, y, i);
        add(y, x, i);
    }
    for (int i = 1; i < n; i ++ ) {
        ans += (ll)(n - i) * (n - i);
    }
    // cout << ans << endl ;
    dfs(1, 1);
    cout << ans << endl ;
    return 0;
}

小结:真难则反真的是好用,而且我们要知道哪些我们能处理,哪些不能处理。

[Comet OJ - Contest #6 C][48C 2279]一道树题_树

标签:typedef   names   复杂   str   alc   端点   char   之间   cpp   

原文地址:https://www.cnblogs.com/ShuraK/p/11241207.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!