zoukankan      html  css  js  c++  java
  • 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;
    }
    
    

    <后记>

  • 相关阅读:
    5.19 省选模拟赛 T1 小B的棋盘 双指针 性质
    5.15 省选模拟赛 容斥 生成函数 dp
    5.15 省选模拟赛 T1 点分治 FFT
    5.15 牛客挑战赛40 B 小V的序列 关于随机均摊分析 二进制
    luogu P4929 【模板】舞蹈链 DLX
    CF 878E Numbers on the blackboard 并查集 离线 贪心
    5.10 省选模拟赛 拍卖 博弈 dp
    5.12 省选模拟赛 T2 贪心 dp 搜索 差分
    5.10 省选模拟赛 tree 树形dp 逆元
    luogu P6088 [JSOI2015]字符串树 可持久化trie 线段树合并 树链剖分 trie树
  • 原文地址:https://www.cnblogs.com/Parsnip/p/11404248.html
Copyright © 2011-2022 走看看