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
*/