[bzoj3648]寝室管理【环套树】【点分治】

【题目描述】

Description

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

Input

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

Output

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

Sample Input

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

Sample Output

20

HINT



N≤100000      

K≤N

M=N           

【题解】

    如果只是一棵树,就是一个裸的点分治模板题。

    考虑环的情况,先把环去掉变成几棵独立的树,在每棵树上跑点分治。

    现在来考虑环上的情况。

    先把环倍长,于是就变成了一条链上,每个点接了一棵树,对于链上后一半的节点,统计每个节点与之前 环长度-1 个链上节点的贡献。可以保证不重复不遗漏。

/* --------------
    user Vanisher
    problem bzoj-3648 
----------------*/
# include <bits/stdc++.h>
# define 	N 		100010
# define 	T		300000
# define 	inf 	1e9
# define 	ll 		long long
using namespace std;
int read(){
	int tmp=0, fh=1; char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') fh=-1; ch=getchar();}
	while (ch>='0'&&ch<='9'){tmp=tmp*10+ch-'0'; ch=getchar();}
	return tmp*fh;
}
struct node{
	int data,next;
}e[N*2];
int h[T+10],low[N],dfn[N],place,head[N],ti,st[N],top,c[N],len,size[N],tag[N],mn,root,q[N],num,n,k,m;
ll ans;
void build(int u, int v){
	e[++place].data=v; e[place].next=head[u]; head[u]=place;
	e[++place].data=u; e[place].next=head[v]; head[v]=place;
}
int lowbit(int x){
	return x&(-x);
}
int query(int x){
	int num=0;
	while (x>0){
		num=num+h[x];
		x=x-lowbit(x);
	} 
	return num;
}
void modify(int x, int num){
	while (x<=T){
		h[x]=h[x]+num;
		x=x+lowbit(x);
	}
}
void tarjan(int x, int fa){
	low[x]=dfn[x]=++ti; st[++top]=x;
	for (int ed=head[x]; ed!=0; ed=e[ed].next)
		if (e[ed].data!=fa){
			if (low[e[ed].data]==false){
				tarjan(e[ed].data,x);
				low[x]=min(low[x],low[e[ed].data]);
			}
			else low[x]=min(low[x],dfn[e[ed].data]);
		}
	if (low[x]==dfn[x]){
		if (st[top]==x) top--;
		else {
			while (st[top]!=x) c[++len]=st[top--];
			c[++len]=st[top--];
			for (int i=1; i<=len; i++) tag[c[i]]=true;
		}
	}
}
void findroot(int x, int fa, int tot){
	size[x]=1; int mx=0;
	for (int ed=head[x]; ed!=0; ed=e[ed].next)
		if (e[ed].data!=fa&&tag[e[ed].data]==false){
			findroot(e[ed].data,x,tot);
			mx=max(mx,size[e[ed].data]);
			size[x]+=size[e[ed].data];
		}
	mx=max(tot-size[x],mx);
	if (mn>mx) mn=mx, root=x;
}
void getdist(int x, int fa, int dist){
	size[x]=1; q[++num]=dist;
	ans=ans+query(n)-query(k-dist-1); 
	st[++top]=dist;
	if (dist>=k) ans++;
	for (int ed=head[x]; ed!=0; ed=e[ed].next)
		if (e[ed].data!=fa&&tag[e[ed].data]==false){
			getdist(e[ed].data,x,dist+1);
			size[x]+=size[e[ed].data];
		}
}
void getdistlen(int x, int fa, int dist){
	modify(dist,1);
	for (int ed=head[x]; ed!=0; ed=e[ed].next)
		if (e[ed].data!=fa&&tag[e[ed].data]==false)
			getdistlen(e[ed].data,x,dist+1);
}
void del(int x, int fa, int la, int now){
	modify(la,-1);
	q[++num]=now;
	for (int ed=head[x]; ed!=0; ed=e[ed].next)
		if (e[ed].data!=fa&&tag[e[ed].data]==false)
			del(e[ed].data,x,la+1,now+1);
}
void solve(int x){
	top=0; tag[x]=true;
	if (k==0) ans++;
	for (int ed=head[x]; ed!=0; ed=e[ed].next)
		if (tag[e[ed].data]==false){
			num=0;
			getdist(e[ed].data,0,1);
			for (int i=1; i<=num; i++) modify(q[i],1);
		}
	for (int i=1; i<=top; i++) modify(st[i],-1);
	for (int ed=head[x]; ed!=0; ed=e[ed].next)
		if (tag[e[ed].data]==false){
			mn=inf;
			findroot(e[ed].data,0,size[e[ed].data]);
			solve(root);
		}
}
int main(){
	n=read(), m=read(), k=read()-1;
	for (int i=1; i<=m; i++){
		int u=read(), v=read();
		build(u,v);
	} 
	if (n!=m){
		mn=inf;
		findroot(1,0,n);
		solve(root);
		printf("%lld\n",ans);
	}
	else{
		tarjan(1,0);
		for (int i=1; i<=len; i++) solve(c[i]);
		memset(tag,0,sizeof(tag));
		for (int i=1; i<=len; i++) tag[c[i]]=1;
		for (int i=1; i<=len; i++)
			getdistlen(c[i],0,2*len-i+1);
		for (int i=len+1; i<=2*len; i++){
			num=0;del(c[i-len],0,3*len-i+1,0);
			for (int j=1; j<=num; j++)
				ans=ans+query(T)-query(k-q[j]+2*len-i);
			for (int j=1; j<=num; j++)
				modify(2*len-i+1+q[j],1);
		}
		printf("%lld\n",ans);
	}
	return 0;
}

    具体实现见代码。


posted @ 2018-02-11 12:32  Vanisher  阅读(249)  评论(0编辑  收藏  举报