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
1 3
2 4
3 5
4 1
5 2
Sample Output
20
题解
如果说题目的限制是n个点n-1条边,那么这是一道傻逼点分治,但是n个点n条边怎么办呢,首先这是一个基环外向树,我们先断掉环上的一条边,然后点分治出答案,这样不过这条边的答案我们就算出来了,然后我们再瞎搞一搞就能算出经过这条边的方案数。(码一码也是挺爽的233.
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#include<iostream>
#include<iomanip>
using namespace std;
struct bian
{
int l,r;
bool ban;
}a[300000];
int fir[300000];
int nex[300000];
int du[300000];
int tot=1;
void add_edge(int l,int r)
{
a[++tot].l=l;
a[tot].r=r;
a[tot].ban=false;
nex[tot]=fir[l];
fir[l]=tot;
du[r]++;
}
int siz[300000];
int fa[300000];
int center;
void get_tree_center(int u,int fro,int zong)
{
fa[u]=fro;
siz[u]=1;
bool pd=true;
for(int o=fir[u];o;o=nex[o])
{
if(a[o].ban || a[o].r==fro) continue;
get_tree_center(a[o].r,u,zong);
siz[u]+=siz[a[o].r];
if(siz[a[o].r]>zong/2) pd=false;
}
if(zong-siz[u]>zong/2) pd=false;
if(pd) center=u;
}
int c[300000];
int tim[300000];
int n,m,k;
long long ans=0;
int T;
void add_val(int t,int val)
{
t++;
while(t)
{
if(tim[t]!=T) c[t]=0;
tim[t]=T;
c[t]+=val;
t-=(t&(-t));
}
}
long long get_val(int t)
{
t++;
long long re=0;
while(t<=n+1)
{
if(tim[t]!=T) c[t]=0;
tim[t]=T;
re+=c[t];
t+=(t&(-t));
}
return re;
}
void dfs1(int u,int fro,int v)
{
ans+=get_val(max(0,k-v));
for(int o=fir[u];o;o=nex[o])
{
if(a[o].r==fro || a[o].ban) continue;
dfs1(a[o].r,u,v+1);
}
}
void dfs2(int u,int fro,int v,int val)
{
add_val(v,val);
for(int o=fir[u];o;o=nex[o])
{
if(a[o].r==fro || a[o].ban) continue;
dfs2(a[o].r,u,v+1,val);
}
}
void tree_divide(int u)
{
int zong=siz[u];
get_tree_center(u,0,siz[u]);
u=center;
if(fa[u]) siz[fa[u]]=zong-siz[u];
T++;
add_val(0,1);
ans+=get_val(k);
for(int o=fir[u];o;o=nex[o])
{
if(a[o].ban) continue;
dfs1(a[o].r,u,1);
dfs2(a[o].r,u,1,1);
}
for(int o=fir[u];o;o=nex[o])
{
if(a[o].ban) continue;
a[o].ban=true;
a[o^1].ban=true;
tree_divide(a[o].r);
}
}
void toposort()
{
static int dui[300000];
int s=1,t=1;
for(int i=1;i<=n;i++)
if(du[i]==1)
dui[t++]=i;
while(s<t)
{
int u=dui[s];
s++;
for(int o=fir[u];o;o=nex[o])
{
du[a[o].r]--;
if(du[a[o].r]==1) dui[t++]=a[o].r;
}
}
}
int h[300000];
int top=0;
bool vis[300000];
int jilu=-1;
void find_huan(int u)
{
vis[u]=true;
h[++top]=u;
for(int o=fir[u];o;o=nex[o])
{
if(vis[a[o].r]) continue;
if(du[a[o].r]>1)
{
if(jilu==-1) jilu=o;
find_huan(a[o].r);
}
}
}
void solve()
{
toposort();
for(int i=1;i<=n;i++)
{
if(du[i]>1)
{
find_huan(i);
break;
}
}
a[jilu].ban=true;
a[jilu^1].ban=true;
siz[1]=n;
tree_divide(1);
T++;
for(int i=2;i<=tot;i++) a[i].ban=false;
for(int i=2;i<=top;i++)
{
add_val(i-1,1);
int u=h[i];
for(int o=fir[u];o;o=nex[o])
{
if(du[a[o].r]>1) continue;
dfs2(a[o].r,u,i,1);
}
}
h[top+1]=h[1];
int t=0;
for(int i=top+1;i>=3;i--)
{
if(i!=top+1)
{
add_val(i-1,-1);
int u=h[i];
for(int o=fir[u];o;o=nex[o])
{
if(du[a[o].r]>1) continue;
dfs2(a[o].r,u,i,-1);
}
}
int u=h[i];
ans+=get_val(max(0,k-t));
for(int o=fir[u];o;o=nex[o])
{
if(du[a[o].r]>1) continue;
dfs1(a[o].r,u,t+1);
}
t++;
}
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
k--;
for(int i=1;i<=m;i++)
{
int l,r;
scanf("%d%d",&l,&r);
add_edge(l,r);
add_edge(r,l);
}
if(m==n-1)
{
siz[1]=n;
tree_divide(1);
cout<<ans<<endl;
return 0;
}
solve();
cout<<ans;
return 0;
}