Description
有个孩子叫小Y,一天,小Y拿到了一个包含n个点和n-1条边的无向连通图,图中的点用1~n的整数编号。小Y突发奇想,想要数出图中有多少个“Y字形”。
一个“Y字形”由5个不同的顶点A、B、C、D、E以及它们之间的4条边组成,其中AB、BC、BD、DE之间有边相连,如下图所示。
同时,无向图中的每条边都是有一定长度的。一个“Y字形”的长度定义为构成它的四条边的长度和。小Y也想知道,图中长度最大的“Y字形”长度是多少。
Input
第一行包含一个整数n,表示无向图的点数。
接下来n行,每行有3个整数x、y、z,表示编号为x和y的点之间有一条长度为z的边。
Output
输出包含2行。
第1行包含一个整数,表示图中的“Y字形”的数量。
第2行包含一个整数,表示图中长度最大的“Y字形”的长度。
Sample Input
7
1 3 2
2 3 1
3 5 1
5 4 2
4 6 3
5 7 3
Sample Output
5
9
HINT
TYVJ2016国庆赛D1T2
【输入输出样例1说明】
图中共有5个“Y字形”,如图中用红色标出的部分所示。
其中,长度最大的“Y字形”是编号3、5、7、4、6的顶点构成的那一个,长度为9。
【数据规模与约定】
对于30%的数据,n≤10
对于60%的数据,n≤2,000
对于100%的数据,n≤200,000,1≤x,y≤n,1≤z≤10,000
以下所有数字以图片中第一个为例。
第一问: 枚举,但要枚举的这一个点,与之构成的图形,最好不要有重复,并且要尽量快。因此,找类似3的中心(因为以它为中心不会有重复,且位于中心位置,循环少),枚举每一个点,以此点为中心,首先此点的度数要先大于等于3。再找要求的图形。 for一遍n个点 再for其相邻的点,记录长度为2的链的个数然后直接公式work()即可; 时间复杂度O(2n)
首先,暴力枚举。如果枚举类似7点的位置会超时。因此,找其中心3 枚举每一个点,并以此点作为中心往外找。
朴素做法,大暴力,找1,2,5,7;
显然,会超时;
优化:记录最大值,次大值,第三大值(中心点有三个度最坏情况下 用三个)。然后用一个pair 记录最值得个数。
已经记录了最大值,如何保证找到的以此点为中心的长度最大。先找长度为2 的链的权值还是先找相邻的两个点权值?
显然,可以证得,先找长度为2 的链再找相邻两点,或先找相邻的最大两个点再找长为2的链,长度最大的值一定为其中之一。
因为若长为2的链不是最长的或相邻两点不是最大的,一定有更大的解。
以此点为中心的图形最长值是上述两种方法的值之一,但不确定是哪一个。因此走两遍即可。
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
#define mp make_pair
#define LL long long
LL n,ans1,ans2;
vector <pair<LL,LL> > v[222222];
pair <LL ,LL > mx[222222][5];
LL ss,s1,s2,s3,s4,s11,s22,s33,s44,s55;
LL gs[222222];
LL sy2[5];
LL getint()
{
LL x=0,f=1; char ch=getchar();
while (ch<'0' || ch >'9') {if (ch=='-') f=-1; ch=getchar();}
while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
LL bj(LL x,LL z)
{
// first second
if(z>mx[x][1].first)
{
mx[x][3]=mx[x][2];
mx[x][2]=mx[x][1];
mx[x][1]=mp(z,1);
}
else
if(z==mx[x][1].first)
mx[x][1].second++;
else
if(z>mx[x][2].first)
{
mx[x][3]=mx[x][2];
mx[x][2]=mp(z,1);
}
else
if(z==mx[x][2].first) mx[x][2].second++;
else
if(z>mx[x][3].first) mx[x][3]=mp(z,1);
else
if(z==mx[x][3].first) mx[x][3].second++;
}
LL work(LL x)
{
return x*(x-1)/2;
}
LL work2(LL now,LL nn,LL sy,LL cf)
{
if(mx[now][nn].first>cf||mx[now][nn].first<cf)
{
LL x=mx[now][nn].second;
if(x>=sy) {s4+=mx[now][nn].first*sy;sy2[int(nn)]-=x;return 0;}
else
{
s4+=mx[now][nn].first*x;
sy2[nn]=0;
work2(now,nn+1,sy-x,cf);
}
}
else
if(mx[now][nn].first==cf)
{
LL x=mx[now][nn].second;
if(x>1)
{
x--;
if(x>=sy)
{s4+=mx[now][nn].first*sy;return 0;}
else
{
s4+=mx[now][nn].first*x;
work2(now,nn+1,sy-x,cf);
}
}
else
{
work2(now,nn+1,sy,cf);
}
}
}
LL work3(LL fa,LL son,LL len,LL nn)
{
if(mx[son][nn].first>len||mx[son][nn].first<len)
{
s1+=mx[son][nn].first;return 0;
}
else
{
if(mx[son][nn].first==len)
{
if(mx[son][nn].second>1)
{
s1+=mx[son][nn].first;return 0;
}
else
{
work3(fa,son,len,nn+1);
}
}
}
}
int pd(int now,int son,int len,int nn)
{
if(nn>3) return 1;
if(len==mx[now][nn].first)
{
if(sy2[nn]>0)
return 1;
else
return 0;
}
else
{
if(len<mx[now][nn].first)
{
return pd(now,son,len,nn+1);
}
}
}
int main()
{
// freopen("question3.in","r",stdin);
// freopen("question9.out","w",stdout);
n=getint();
for(int i=1;i<n;i++)
{
LL x=getint(),y=getint(),z=getint();
bj(x,z);
bj(y,z);
v[x].push_back(mp(y,z));
v[y].push_back(mp(x,z));
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<v[i].size();j++)
{
LL son=v[i][j].first;
if((LL)( v[son].size() )>1)
gs[i]+=v[son].size()-1;
}
}
for(int i=1;i<=n;i++)
{
// cout<<gs[i]<<endl;
if(v[i].size()>=3)
{
ss=0;
ss=work(v[i].size()-1);
ans1+=ss*gs[i];
// work2(i,1,3);
//s1=0;
s1=s2=s3=s4=s11=s22=s33=s44=0;//初始化
for(int j=0;j<v[i].size();j++)/********第一次找:begin. 先找最大的长度为2的链 再找不重复的两个相邻点*******/
{
s1=v[i][j].second;
work3(i,v[i][j].first,v[i][j].second,1);
if(s1>s2)
{
s2=s1;
s3=v[i][j].second;
}
}
work2(i,1,2,s3);
/***********第一次找:end********************/
// s11=s2;
ans2=max(ans2,s4+s2);
s1=s2=s3=s4=0;//初始化/***********第二次找:begin。 先找最大的两个相邻点,再找不重复的最大的长度为2的链 **********/
for(int j=1;j<=3;j++)
sy2[j]=mx[i][j].second;
work2(i,1,2,0);
for(int j=0;j<v[i].size();j++)
{
s1=v[i][j].second;
if(pd(i,v[i][j].first,v[i][j].second,1))
{
work3(i,v[i][j].first,v[i][j].second,1);
if(s1>s2)
{
s2=s1;
s3=v[i][j].second;
}
}
}/***********第二次找:end。*************/
// for(int j=0;j<v[i].size();j++)/*************此处为找次大的长度为2 的链。有数据3过不了(之前只找了第一次)*********/
// {
// s1=v[i][j].second;
// work3(i,v[i][j].first,v[i][j].second,1);
// if(s1>s2&&s1<s11)
// {
// s2=s1;
// s3=v[i][j].second;
// }
// }
// work2(i,1,2,s3);
ans2=max(ans2,s4+s2);
}
}
cout<<ans1<<endl<<ans2<<endl;
return 0;
}
/*
7
1 3 2
2 3 1
3 5 1
5 4 2
4 6 3
5 7 3
*/
/*
10
1 2 40
1 3 80
2 4 81
2 5 81
3 6 21
5 7 45
6 8 68
7 9 41
5 10 72
6
282
*/