『寝室管理 基环树点分』


寝室管理

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\n",ans);
    return 0;
}


『寝室管理 基环树点分』

原文地址:https://www.cnblogs.com/Parsnip/p/11404248.html

时间: 2024-10-08 08:54:06

『寝室管理 基环树点分』的相关文章

【BZOJ-3648】寝室管理 环套树 + 树状数组 + 点分治

3648: 寝室管理 Time Limit: 40 Sec  Memory Limit: 512 MBSubmit: 239  Solved: 106[Submit][Status][Discuss] Description T64有一个好朋友,叫T128.T128是寄宿生,并且最近被老师叫过去当宿管了.宿管可不是一件很好做的工作,碰巧T128有一个工作上的问题想请T64帮忙解决.  T128的寝室条件不是很好,所以没有很多钱来装修.礼间寝室仅由n-1条双向道路连接,而且任意两间寝室之间都可以互

BZOJ 3648: 寝室管理( 点分治 + 树状数组 )

1棵树的话, 点分治+你喜欢的数据结构(树状数组/线段树/平衡树)就可以秒掉, O(N log^2 N). 假如是环套树, 先去掉环上1条边, 然后O(N log^2 N)处理树(同上); 然后再O(N log N)计算经过删掉边的路径数(在环上扫一遍, 数据结构维护). ------------------------------------------------------------------------- #include<cstdio> #include<cctype>

【BZOJ3648】寝室管理 树分治

[BZOJ3648]寝室管理 Description T64有一个好朋友,叫T128.T128是寄宿生,并且最近被老师叫过去当宿管了.宿管可不是一件很好做的工作,碰巧T128有一个工作上的问题想请T64帮忙解决.T128的寝室条件不是很好,所以没有很多钱来装修.礼间寝室仅由n-1条双向道路连接,而且任意两间寝室之间都可以互达.最近,T128被要求对一条路径上的所有寝室进行管理,这条路径不会重复经过某个点或某条边.但他不记得是哪条路径了.他只记得这条路径上有不少于k个寝室.于是,他想请T64帮忙数

『Balancing Act 树的重心』

树的重心 我们先来认识一下树的重心. 树的重心也叫树的质心.找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡. 根据树的重心的定义,我们可以通过树形DP来求解树的重心. 设\(Max_i\)代表删去i节点后树中剩下子树中节点最多的一个子树的节点数.由于删去节点i至少将原树分为两部分,所以满足\(\ \frac{1}{2} \leq Max_i\),我们要求的就是一个\(i\),使得\(Max_i\)最小. 对于Max数组,我们可以列出

『Andrew and Chemistry 树同构』

Andrew and Chemistry Description During the chemistry lesson Andrew learned that the saturated hydrocarbons (alkanes) enter into radical chlorination reaction. Andrew is a very curious boy, so he wondered how many different products of the reaction m

『战略游戏 最大利润 树形DP』

通过两道简单的例题,我们来重新认识树形DP. 战略游戏(luoguP1026) Description Bob喜欢玩电脑游戏,特别是战略游戏.但是他经常无法找到快速玩过游戏的办法.现在他有个问题.他要建立一个古城堡,城堡中的路形成一棵树.他要在这棵树的结点上放置最少数目的士兵,使得这些士兵能了望到所有的路.注意,某个士兵在一个结点上时,与该结点相连的所有边将都可以被了望到. 请你编一程序,给定一树,帮Bob计算出他需要放置最少的士兵. Input Format 输入文件中数据表示一棵树,描述如下

『2020第一次考试总结及规划』

多校联合测试 一场常规省选难度的模拟赛,三题的标签分别是"结论题","多项式","费用流". 第一题看似是数位题,但是卡空间,并且删除操作难以用\(\mathrm{Trie}\)等数据结构维护,让人匪夷所思. 正解是一个结论,值域区间最大的\(\mathrm{and}\)和必然诞生在前\(p\)大的数当中,\(p\)与值域的一个\(\log\)同阶.证明可以考虑反证法,分成\(\mathrm{ans}\)最高位以上的位和最高位以下的位两部分考虑即

BZOJ 1040: [ZJOI2008]骑士(基环树dp)

http://www.lydsy.com/JudgeOnline/problem.php?id=1040 题意: 思路: 这是基环树,因为每个人只会有一个厌恶的人,所以每个节点只会有一个父亲节点,但是根节点也是有父亲节点的,所以在树中肯定是存在一个环的,只要删除该环中的任意一条边,那么就能将该图变成一颗树. 如果是树的话,那就很简单了,d[u][0/1] dp求解即可. 现在假设删除的边是e,两端的节点分别是u,v,首先对u为根的树作一次dp,最后取d[u][0](v取不取都无所谓),不能取d[

2018智慧树知到创业管理答案创业管理智慧树答案

智慧树知到 创业管理答案 创业管理智慧树答案 智慧树知到创业管理答案 1 ************************* 完整答案q:.64315052 ************************* [多选题](5分) 以下哪些是冒险型创业的特征ABC A. 从事一项全新的产品经营,个人前途的不确定性也很大 B. 由于是创造新价值的活动,将面临较高的失败可能性 C. 创业预期的报酬较高 D. 创业需要的资本金较多 2 [多选题](5分) 以下哪些是创业过程的阶段 A. 产生创业动机/决