【题目描述】
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
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 ",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 ",ans); } return 0; }
具体实现见代码。