『寝室管理 基环树点分』

Parsnip·2019-08-24 12:43·190 次阅读

『寝室管理 基环树点分』

<更新提示>

<第一次更新>


<正文>

寝室管理#

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#

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

Sample Output#

Copy
20

解析#

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

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

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

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

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

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

\(Code:\)

Copy
#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; }

<后记>

posted @   Parsnip  阅读(190)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
目录