• [Comet OJ


    一道树题

    题目大意

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

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

    数据范围:$2le Nle 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
    ", 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;
    }
    

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

  • 相关阅读:
    资源合并fis-postpackager-simple插件的使用
    FIS3使用官方例子流程
    FIS常用命令
    SASS输出风格
    Webstorm实时编译SASS和LESS
    如何使用Less?
    DNS预解析dns-prefetch提升页面载入速度优化前端性能
    前端性能优化策略
    利用多域名存储静态资源进行性能优化:网站的静态资源为什么要使用独立域名
    Nginx多域名配置
  • 原文地址:https://www.cnblogs.com/ShuraK/p/11241207.html
Copyright © 2020-2023  润新知