2019HDU多校赛第九场G、Rikka with Travels(树的直径)

Rikka with Travels

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 627    Accepted Submission(s): 204


 

Problem Description

To enjoy their summer vacations, Rikka and Yuta decide to go travels. According to past experiences, contradictions always arose when they were planning for the same trip. This time, they decide to make plans dividually and will go travel twice.

Coincidentally, they choose the same country Fantasy as the destination, which is a small island country on the Pacific. There are n cities in Fantasy and they are connected with n−1 two-way roads. It is guaranteed that any two cities can reach each other by the road system.

Though Rikka and Yuta love travels, visiting the same city more than once is still boring for them. Therefore, both Rikka and Yuta choose a simple path (i.e., a path without visiting any city more than once) as her/his plan. Moreover, they want to ensure the two paths do not intersect on any city.

Suppose Rikka chooses the path from a to b, Yuta chooses the path from c to d (both a=b and c=d are allowed), they define the feature of the plan is an ordered pair (L(a,b),L(c,d)), where L(x,y) represents the number of cities on the path from x to y.

Now, Rikka wants to count the number of different features, i.e., the number of different integer pairs (l1,l2) which satisfies there exists a valid travel plan (a,b,c,d)meets L(a,b)=l1,L(c,d)=l2. Since Rikka and Yuta are busy with planning their trip, Rikka asks you to help her calculate the answer.

Input

The first line of the input contains a single integer T(1≤T≤300), the number of test cases.

For each test case, the first line contains a single integer n(1≤n≤105), the number of cities in Fantasy.

Then n−1 lines follow. Each line contains two integers ui,vi(1≤ui,vi≤n) which represents a two-way road (ui,vi) in the road system.

The input guarantees that there are no more than 5 test cases with n>500.

Output

For each test case, output a single line with a single integer, the answer.

Hint

In the first test case, the possible features are (1,1),(1,2),(1,3),(2,1),(3,1). Therefore the answer is 5.

In the second test case, the possible features are (1,1),(1,2),(1,3),(1,4),(2,1),(2,2),(2,3),(3,1),(3,2),(4,1). Therefore the answer is 10.

Sample Input

2

4

1 2

1 3

1 4

5

1 2

2 3

3 4

3 5

Sample Output

5

10

 

 

题意

给出一个n个点的树,让你计算本质不同的两条不点相交的路径的边权和的有序对的数量。

解释:

这里的边权和是指路径上点的数量;

本质不同的有序对,如:(1,2)和(2,1)   ,    (1,5)和(4,2).....(显然(1,2)和(1,2)是本质相同的)

边权和的有序对:设一条路径的边权和为u,另一条路径的边权和为v,则它们组成的边权和的有序对为(u,v)

不点相交的路径:两条路径不共点

 

实在无法理解的话就参考样例吧。。。(其实我第一次看题时也没看懂)

 

题解

特别锻炼码力的一道题。

比较好想的是:当我们算出一个(u,v)时,所有的(u,v),(u-1,v),(u,v-1),(u-1,v-1),(u-2,v)......都可以直接进行统计

所以我们要让选出来的两条路径尽量长

首先可以想到枚举一条路径,计算把它删掉后剩下的森林的最大直径。

于是我们就可以继续推理:

如果枚举的路径不包含原树直径上的点,那么删掉该路径后,剩下的森林的最大直径就是原树直径。

如果枚举的路径包含原树直径上的点,那么删掉该路径后,剩余森林的最大直径的一个端点一定是原树直径的某一个端点。

但是这样做并不能使红色的路径最长,所以在这种情况下,红色的路径也会有一个端点是直径的某一个端点。

然后我们发现可以枚举直径上断开的边,然后分别求两个子树的直径,来更新答案(注意!在代码中是cas1())

别忘了第一种情况也要更新答案(注意!在代码中是cas2())

 

 

具体怎么做呢?

先O(n)求出树的直径并把它剥离出来

O(n)预处理出每一个点 i 分别到原树两个直径端点的距离dis[i][0]和dis[i][1]

因为cas1()中的两棵子树的直径都会分别包含一个原树直径端点。

然后就可以对原树直径上的点求一个前缀max和一个后缀max,放入答案数组即可。

这样就完成了cas1()

 

对于cas2(),我们只需要求删除直径后每棵子树的直径就好了,O(n)。

 

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
int n;
#define N 100005
int fir[N],to[2*N],nxt[2*N],cnt;
inline void adde(int a,int b)
{
	to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;
	to[++cnt]=a;nxt[cnt]=fir[b];fir[b]=cnt;
}
int tmpdep[N],tmpfa[N];
bool vis[N];

//long long cnt1,cnt2,cnt3;


inline void dfs(int u,int fa,int deep)
{
	
	//cnt1++;
	
	tmpfa[u]=fa;
	tmpdep[u]=deep;
	int v,p;
	for(p=fir[u];p;p=nxt[p]){
		v=to[p];
		if(fa!=v&&!vis[v])
			dfs(v,u,deep+1);
	}
}
int D[N][3],dis[N][2],A,B,len;
inline void splitD()
{
	int i;
	A=0;B=0;len=0;
	dfs(1,0,1);for(i=1;i<=n;i++)if(tmpdep[A]<tmpdep[i])A=i;
	dfs(A,0,1);for(i=1;i<=n;i++)if(tmpdep[B]<tmpdep[i])B=i;
	int tmp=B;while(tmp){D[++len][0]=tmp;tmp=tmpfa[tmp];}
	for(i=1;i<=n;i++)dis[i][0]=tmpdep[i];
	dfs(B,0,1);for(i=1;i<=n;i++)dis[i][1]=tmpdep[i];
	for(i=1;i<=len;i++)vis[D[i][0]]=1;// !!!!!!!!!
}
int mx,anscnt;
struct node{
	int x,y;
	node(){}
	node(int a,int b){x=a;y=b;}
}a[2*N];
inline void dfs2(int u,int fa,int flg)
{
	
	//cnt2++;
	
	mx=max(mx,dis[u][flg]);
	int v,p;
	for(p=fir[u];p;p=nxt[p]){
		v=to[p];
		if(fa!=v&&!vis[v])
			dfs2(v,u,flg);
	}
}
inline void cas1()
{
	int i;anscnt=0;D[1][1]=1;D[len][2]=1;
	for(i=2;i<=len;i++){
		mx=0;dfs2(D[i][0],0,1);
		D[i][1]=max(D[i-1][1],mx);
	}
	for(i=len-1;i>=1;i--){
		mx=0;dfs2(D[i][0],0,0);
		D[i][2]=max(D[i+1][2],mx);
	}
	for(i=1;i<len;i++){
		a[++anscnt].x=D[i][1];
		a[anscnt].y=D[i+1][2];
		a[++anscnt].y=D[i][1];
		a[anscnt].x=D[i+1][2];
	}
}
int ttmp;
inline void dfs3(int u,int fa,int deep)
{
	
	//cnt3++;
	
	if(deep>mx){mx=deep;ttmp=u;}
	int v,p;
	for(p=fir[u];p;p=nxt[p]){
		v=to[p];
		if(fa!=v&&!vis[v])
			dfs3(v,u,deep+1);
	}
}
inline int get_d(int s)
{
	mx=0;ttmp=0;dfs3(s,0,1);
	int tmp=ttmp;
	mx=0;ttmp=0;dfs3(tmp,0,1);
	return mx;
}
inline void cas2()
{
	int i,p,cmx=0;
	for(i=1;i<=len;i++)
		for(p=fir[D[i][0]];p;p=nxt[p])
			if(!vis[to[p]])
				cmx=max(cmx,get_d(to[p]));
	a[++anscnt].x=cmx;a[anscnt].y=len;
	a[++anscnt].y=cmx;a[anscnt].x=len;
}
inline bool cmp(node q,node w){return q.x<w.x||(q.x==w.x&&q.y<w.y);}
int stk[2*N],top;
long long lastans;
inline void calc()
{
	sort(a+1,a+anscnt+1,cmp);
	int i;top=0;lastans=0;
	for(i=1;i<=anscnt;i++){
		while(top>0&&a[stk[top]].y<=a[i].y)
			top--;
		stk[++top]=i;
	}
	for(i=1;i<=top;i++)
		lastans+=1ll*(a[stk[i]].x-a[stk[i-1]].x)*a[stk[i]].y;
}
//#include<ctime>
int main()
{
	//freopen("1.in","r",stdin);
	int T,i,u,v;
	
	//double c1,c2;
	
	scanf("%d",&T);
	while(T--){
		
		//cnt1=cnt2=cnt3=0;
		cnt=0;
		scanf("%d",&n);
		for(i=1;i<=n;i++)vis[i]=fir[i]=0;
		for(i=1;i<n;i++){
			scanf("%d%d",&u,&v);
			adde(u,v);
		}
		//c1=clock();
		splitD();//c2=clock();printf("splitD: %lf\n",c2-c1);c1=clock();
		cas1();//c2=clock();printf("cas1: %lf\n",c2-c1);c1=clock();
		cas2();//c2=clock();printf("cas2: %lf\n",c2-c1);c1=clock();
		calc();//c2=clock();printf("calc: %lf\n",c2-c1);
		//printf("len=%d\ncnt1=%lld,cnt2=%lld,cnt3=%lld\n",len,cnt1,cnt2,cnt3);
		printf("%lld\n",lastans);
	}
}

 

 

 

 

终于A了。。。

 

 

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值