The K-th Distance
Time Limit: 8000/4000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 202 Accepted Submission(s): 50
Problem Description
Given a tree, which has n node in total. Define the distance between two node u and v is the number of edge on their unique route. So we can have n(n-1)/2 numbers for all the distance, then sort the numbers in ascending order. The task is to output the sum of the first K numbers.
Input
There are several cases, first is the number of cases T. (There are most twenty cases).
For each case, the first line contain two integer n and K ( 2≤n≤100000,0≤K≤min(n(n−1)/2,106) ). In following there are n-1 lines. Each line has two integer u , v. indicate that there is an edge between node u and v.
For each case, the first line contain two integer n and K ( 2≤n≤100000,0≤K≤min(n(n−1)/2,106) ). In following there are n-1 lines. Each line has two integer u , v. indicate that there is an edge between node u and v.
Output
For each case output the answer.
Sample Input
2 3 3 1 2 2 3 5 7 1 2 1 3 2 4 2 5
Sample Output
4 10
Source
给定一个n个节点的树,求路径长度前k大的所有路径和。
题解方法:把所有边(u,v) 以及(v,u)放入一个队列,队列每弹出一个元素(u,v),对于所有与u相邻的点w,如果w!=v,就把(w,u)入队。这样就能一个一个生成前K小的距离。注意到每条边实际上会入队两次,只要把K翻倍且把ans除2即可,时间复杂度为O(n+K)
由于思路简单,就懒得写代码了,直接抄了yyn代码:
#include <map>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std ;
typedef long long LL ;
#define rep( i , a , b ) for ( int i = ( a ) ; i < ( b ) ; ++ i )
#define For( i , a , b ) for ( int i = ( a ) ; i <= ( b ) ; ++ i )
#define rev( i , a , b ) for ( int i = ( a ) ; i >= ( b ) ; -- i )
#define clr( a , x ) memset ( a , x , sizeof a )
const int MAXN = 100005 ;
const int MAXE = 200005 ;
struct Node {
int v , p , d ;
Node () {}
Node ( int v , int p , int d ) : v ( v ) , p ( p ) , d ( d ) {}
} ;
struct Edge {
int v , n ;
Edge () {}
Edge ( int v , int n ) : v ( v ) , n ( n ) {}
} ;
Node Q[3000005] ;
Edge E[MAXE] ;
int H[MAXN] , cntE ;
int head , tail ;
LL ans ;
int n , k ;
void clear () {
ans = 0 ;
cntE = 0 ;
clr ( H , -1 ) ;
}
void addedge ( int u , int v ) {
E[cntE] = Edge ( v , H[u] ) ;
H[u] = cntE ++ ;
}
void bfs () {
int cnt = 0 ;
head = tail = 0 ;
For ( i , 1 , n ) Q[tail ++] = Node ( i , 0 , 0 ) ;
while ( head != tail ) {
Node x = Q[head ++] ;
int u = x.v , p = x.p ;
for ( int i = H[u] ; ~i ; i = E[i].n ) {
int v = E[i].v ;
if ( v != p ) {
Q[tail ++] = Node ( v , u , x.d + 1 ) ;
ans += x.d + 1 ;
//printf ( "%d->%d %d %d\n" , u , v , cnt , x.d + 1 ) ;
++ cnt ;
if ( cnt == k ) return ;
}
}
}
}
void solve () {
int u , v ;
clear () ;
scanf ( "%d%d" , &n , &k ) ;
k *= 2 ;
rep ( i , 1 , n ) {
scanf ( "%d%d" , &u , &v ) ;
addedge ( u , v ) ;
addedge ( v , u ) ;
}
bfs () ;
printf ( "%I64d\n" , ans / 2 ) ;
}
int main () {
int T ;
scanf ( "%d" , &T ) ;
while ( T -- ) solve () ;
return 0 ;
}
还有一种方法,按照树分治考虑,由于k最大为1000000,那么第k长的路径长度必然数据不是很大,我们可以估计一个上限,然后按照树分治的做法,统计每一个长度的方案数,对于每一个分治重心,枚举每一颗子树,然后统计方案数,最后扫描一遍就行了。
代码:
/* ***********************************************
Author :rabbit
Created Time :2014/11/9 12:03:22
File Name :H.cpp
************************************************ */
#pragma comment(linker, "/STACK:102400000,102400000")
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <sstream>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <string>
#include <time.h>
#include <math.h>
#include <queue>
#include <stack>
#include <set>
#include <map>
using namespace std;
#define INF 0x3f3f3f3f
#define eps 1e-8
#define pi acos(-1.0)
typedef long long ll;
const int maxn=100100;
int head[maxn],tol;
int size[maxn],vis[maxn],fa[maxn],que[maxn];
int num[maxn],ans[maxn];
int maxd,TT;
struct Edge{
int next,to;
}edge[3*maxn];
inline void addedge(int u,int v){
edge[tol].to=v;
edge[tol].next=head[u];
head[u]=tol++;
}
inline int getroot(int u){
int Min=maxn,root=0;
int l,r;
que[l=r=1]=u;fa[u]=u;
for(;l<=r;l++)
for(int i=head[que[l]];i!=-1;i=edge[i].next){
int v=edge[i].to;
if(v==fa[que[l]]||vis[v]==TT)continue;
que[++r]=v;
fa[v]=que[l];
}
for(l--;l;l--){
int x=que[l],Max=0;
size[x]=1;
for(int i=head[x];i!=-1;i=edge[i].next){
int v=edge[i].to;
if(v==fa[x]||vis[v]==TT)continue;
Max=max(Max,size[v]);
size[x]+=size[v];
}
Max=max(Max,r-size[x]);
if(Max<Min){
Min=Max;root=x;
}
}
return root;
}
int dis[maxn];
int Q[1001000],S[500100];
inline void go(int root){
int tot=0;
for(int i=head[root];i!=-1;i=edge[i].next){
int u=edge[i].to;
if(vis[u]==TT)continue;
fa[u]=root;
dis[u]=1;
int l,r,cnt=0;
que[l=r=1]=u;
for(;l<=r;l++){
int x=que[l];
ans[dis[x]]++;
Q[cnt++]=dis[x];
for(int j=1;j<=maxd;j++)
if(j+dis[x]<=maxd)ans[j+dis[x]]+=num[j];
for(int j=head[x];j!=-1;j=edge[j].next){
int v=edge[j].to;
if(v==fa[x]||vis[v]==TT||dis[x]+1>maxd)continue;
dis[v]=dis[x]+1;
fa[v]=x;
que[++r]=v;
}
}
for(int j=0;j<cnt;j++){
if(!num[Q[j]])S[tot++]=Q[j];
num[Q[j]]++;
}
}
for(int i=0;i<tot;i++)num[S[i]]=0;
}
inline void solve(int u){
int root=getroot(u);
vis[root]=TT;
go(root);
for(int i=head[root];i!=-1;i=edge[i].next){
int v=edge[i].to;
if(vis[v]==TT)continue;
solve(v);
}
}
int main()
{
//freopen("data.in","r",stdin);
//freopen("data.out","w",stdout);
int T,n,k;
cin>>T;
memset(vis,0,sizeof(vis));
while(T--){
scanf("%d%d",&n,&k);
maxd=min(k/n+50,k);
memset(head,-1,sizeof(head));tol=0;
memset(num,0,sizeof(num));
memset(ans,0, sizeof(ans));
TT++;
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v);
addedge(v,u);
}
solve(1);
ll sum=0;
for(int i=1;i<=maxd;i++){
if(ans[i]>=k){
sum+=(ll)k*i;
break;
}
k-=ans[i];
sum+=(ll)ans[i]*i;
}
printf("%I64d\n",sum);
}
return 0;
}