• sss


    <更新提示>

    <第一次更新>


    <正文>

    寝室管理

    Description

    T64有一个好朋友,叫T128。T128是寄宿生,并且最近被老师叫过去当宿管了。

    宿管可不是一件很好做的工作,碰巧T128有一个工作上的问题想请T64帮忙解决。T128的寝室条件不是很好,所以没有很多钱来装修。礼间寝室仅由n-1条双向道路连接,而且任意两间寝室之间都可以互达。最近,T128被要求对一条路径上的所有寝室进行管理,这条路径不会重复经过某个点或某条边。但他不记得是哪条路径了。他只记得这条路径上有不少于k个寝室。于是,他想请T64帮忙数一下,有多少条这样的路径满足条件。嗯…还有一个问题。由于最近有一些熊孩子不准晚上讲话很不爽,他们决定修筑一条“情报通道”,如果通道建成,寝室就变成了一个N个点N条边的无向图。并且,经过“情报通道”的路径也是合法的。

    T128心想:通道建成之前,T64还有一个高效的算法帮我数路径条数,但是通道建成之后,他还有办法吗? 对,T64手忙脚乱,根本数不清有多少条路径。于是他找到了你。

    Input Format

    第一行为三个正整数N,M,K(2 ≤ K ≤ N),代表有n间寝室,m条边连接它们,m= n-1意味着“情报遁道”未被修好;m=n意味着“情报通道”已被修好),以及题目描述中的K。

    接下来m行,每行两个正整数z,y,代表第x间寝室与第y间寝室之间有一条双向边。

    Output Format

    仅包含一个整数,代表经过至少K间寝室的路径条数。

    Sample Input

    5 5 2
    1 3
    2 4
    3 5
    4 1
    5 2
    

    Sample Output

    20
    

    解析

    如果不考虑那条情报通道,那就是一个点分治模板题,没什么难度,并且可以拿到(50)分的高分。

    现在变成基环树上的路径统计,好像不能直接点分治了。那就考虑一下基环树的套路,一般来说,基环树问题可以有两种方法化简:(1.) 强制拆一条边,按照树的方式计算,然后计算强制加入这条边的贡献。(2.) 每次枚举一条环上的边拆掉,然后分别计算贡献,最后合并答案。

    对于这道题,其实两种方法都可以使用,不过显然使用第一种会比较简单。

    我们可以先找到基环树的环,然后把环上的一条边断开,对整棵树进行点分治,那么没有统计的贡献就只剩下了包含这条边的路径。

    考虑如何统计这些路径,可以枚举环上的点,和点分治计算贡献一样用树状数组把半条路径的贡献存起来,然后找另外半条路径的时候计算贡献即可,使这两边的路径中间恰好有我们刚刚删去的那条边,这样就强制把这条边选进去了。

    细节有点多,码量有点大,多看看是不是有点算重复了。

    (Code:)

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 100020 , INF = 0x3f3f3f3f;
    inline int read(void)
    {
    	int x = 0 , w = 0; char ch = ' ';
    	while ( !isdigit(ch) ) w |= ch == '-' , ch = getchar();
    	while ( isdigit(ch) ) x = x * 10 + ch - 48 , ch = getchar();
    	return w ? -x : x;
    }
    struct edge { int ver,next; } e[2*N];
    int n,m,k,t,Head[N],fa[N],vis[N],loop[N],cnt,num;
    int Max[N],size[N],dis[N],flag[N],root,tot,ban;
    long long ans;
    struct BinaryIndexedTree
    {
        int c[N];
        inline int lowbit(int x) { return x & (-x); }
        inline void insert(int p,int v) { for (;p<=n;p+=lowbit(p)) c[p] += v; }
        inline int query(int p)
        {
            if ( p <= 0 ) return 0;
            int res = 0;
            for (;p;p-=lowbit(p)) res += c[p];
            return res;
        }
    } Tree;
    inline void insert(int x,int y)
    {
        e[++t] = (edge){y,Head[x]} , Head[x] = t;
        e[++t] = (edge){x,Head[y]} , Head[y] = t;
    }
    inline void input(void)
    {
        t = 1;
        n = read() , m = read() , k = read();
        for (int i=1;i<=m;i++)
            insert( read() , read() );
    }
    inline void dp(int x,int f)
    {
        size[x] = 1 , Max[x] = 0;
        for (int i=Head[x];i;i=e[i].next)
        {
            int y = e[i].ver;
            if ( y == f || flag[y] ) continue;
            if ( i == ban || (i^1) == ban ) continue;
            dp( y , x );
            size[x] += size[y];
            Max[x] = max( Max[x] , size[y] );
        }
        Max[x] = max( Max[x] , tot - size[x] );
        if ( Max[x] < Max[root] ) root = x;
    }
    inline void dfs(int x,int f)
    {
        for (int i=Head[x];i;i=e[i].next)
        {
            int y = e[i].ver;
            if ( y == f || flag[y] ) continue;
            if ( i == ban || (i^1) == ban ) continue;
            dis[y] = dis[x] + 1;
            dfs( y , x );
        }
    }
    inline void calc(int x,int f)
    {
        ans += Tree.query( n ) - Tree.query( k - dis[x] );
        for (int i=Head[x];i;i=e[i].next)
        {
            int y = e[i].ver;
            if ( y == f || flag[y] ) continue;
            if ( i == ban || (i^1) == ban ) continue;
            calc( y , x );
        }
    }
    inline void update(int x,int f,int v)
    {
        Tree.insert( dis[x] , v );
        for (int i=Head[x];i;i=e[i].next)
        {
            int y = e[i].ver;
            if ( y == f || flag[y] ) continue;
            if ( i == ban || (i^1) == ban ) continue;
            update( y , x , v );
        }
    }
    inline void divide(int x)
    {
        flag[x] = true , dis[x] = 1;
        dfs( x , 0 );
        Tree.insert( 1 , 1 );
        for (int i=Head[x];i;i=e[i].next)
        {
            int y = e[i].ver;
            if ( flag[y] ) continue;
            if ( i == ban || (i^1) == ban ) continue;
            calc( y , x ) , update( y , x , 1 );
        }
        for (int i=Head[x];i;i=e[i].next)
        {
            int y = e[i].ver;
            if ( flag[y] ) continue;
            if ( i == ban || (i^1) == ban ) continue;
            update( y , x , -1 );
        }
        Tree.insert( 1 , -1 );
        for (int i=Head[x];i;i=e[i].next)
        {
            int y = e[i].ver;
            if ( flag[y] ) continue;
            if ( i == ban || (i^1) == ban ) continue;
            tot = size[y] , root = 0;
            dp( y , 0 );
            divide( root );
        }
    }
    inline void findloop(int x)
    {
        vis[x] = ++num;
        for (int i=Head[x];i;i=e[i].next)
        {
            int y = e[i].ver;
            if ( y == fa[x] ) continue;
            if ( vis[y] )
            {
                if ( vis[y] < vis[x] ) continue;
                loop[++cnt] = y;
                for (;y!=x;y=fa[y]) loop[++cnt] = fa[y];
            }
            else fa[y] = x , findloop( y );
        }
    }
    inline void update_(int x,int f,int op,int v)
    {
        Tree.insert( dis[x] + v , op );
        for (int i=Head[x];i;i=e[i].next)
        {
            int y = e[i].ver;
            if ( y == f || flag[y] ) continue;
            update_( y , x , op , v );
        }
    }
    inline void calc_(int x,int f,int v)
    {
        ans += Tree.query( n ) - Tree.query( k - dis[x] - v - 1 );
        for (int i=Head[x];i;i=e[i].next)
        {
            int y = e[i].ver;
            if ( y == f || flag[y] ) continue;
            calc_( y , x , v );
        }
    }
    inline void solve(void)
    {
        for (int i=Head[loop[1]];i;i=e[i].next)
            if ( e[i].ver == loop[cnt] )
                { ban = i; break; }
        root = 0 , tot = n , Max[0] = INF;
        dp( 1 , 0 ) , divide( root );
        ban = 0;
        memset( flag , 0 , sizeof flag );
        for (int i=1;i<=cnt;i++)
            flag[loop[i]] = true;
        for (int i=1;i<=cnt;i++)
        {
            dis[loop[i]] = 1;
            dfs( loop[i] , 0 );
        }
        for (int i=1;i<=cnt;i++)
            update_( loop[i] , 0 , 1 , i );
        for (int i=cnt;i>=1;i--)
            update_( loop[i] , 0 , -1 , i ),
            calc_( loop[i] , 0 , cnt - i - 1 );
    }
    int main(void)
    {
        input();
        if ( m == n-1 )
        {
            root = 0 , tot = n , Max[0] = INF;
            dp( 1 , 0 );
            divide( root );
        }
        else findloop(1) , solve();
        printf("%lld
    ",ans);
        return 0;
    }
    
    

    <后记>

  • 相关阅读:
    Netty学习路线总结
    Intellij IDEA 快捷键整理-鬼畜版(全键盘开发指南)
    httpClient
    [翻译]Javaslang 介绍
    Docker学习<一>--初体验Windows环境下安装
    Spring @RequestParam @RequestBody @PathVariable 等参数绑定注解详解
    IntelliJ IDEA 2016.2激活方法
    解决mac升级后,出现的 xcrun: error: invalid active developer path, missing xcrun 错误
    Logstash 安装与配置
    【JVM学习笔记】双亲委托机制存在的意义
  • 原文地址:https://www.cnblogs.com/Parsnip/p/11404248.html
Copyright © 2020-2023  润新知