(一遍过sample,高兴的我去测试,80 qwq。。。。。。卡常。。。。。。)
AC程序
30的方法:
看数据:n<=10
这个数据直接打暴力完全ok,枚举四条边,看是否能满足Y字的要求就行;
60的方法:
数据:n<=2000
这个数据大约能承受O(N^2)的复杂度;
我们不妨枚举度数为三及以上(有三条以上的边与之相连接)的点:
在图中可以看出B的度数为三:
由B延伸出三条边:我们可以分别把AB,BC,BD作为中心边;
然后枚举B点除BD边外的边的个数(假设个数为X),选出两条作为一种方案,根据组合数的排列公式,共有:x*(x-1)/2种方案,枚举D相连(除BD外)的边的个数(假设个数为Y),根据乘法原则:便可以得出Y的方案数:
x*(x-1)/2*y;
这样第一问就解决了;
第二问:最大的Y边和的权值:
我们可以枚举B点除BD边外边的最大值和次大值,和D点除BD外边的最大权值,加上BD,就是此时的最大权值和。
AC做法
数据:n<=200000
显然承受不了O(N^2)的复杂度.................
我们可以用O(N)的算法,优化一下60的算法;
我们可以对B点和D点的边数进行预处理;
那么Y字数的个数公式就可以是:
(degree[B]-1)(degree[B]-2)/2*(degree[D]-1);
第二问的话:
我们可以统计出每个点的最大值,次大值,第三大值;那么显然就把枚举最大值的复杂度变为了O(1);
那么这个算法的复杂度就变为了O(N),可以一遍过。
下面是AC程序,可借鉴(qwq)
#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;
ll n,x,y,z,num,dg[300000],fl[300000][4],ans;
ll ff[300000][4],sum,flagg;
ll head[1500000],next[1500000],to[1500000],v[1500000],ans1;
void add1(ll a,ll b,ll c)
{
num++;
next[num]=head[a];
to[num]=b;
v[num]=c;
head[a]=num;
num++;
next[num]=head[b];
to[num]=a;
v[num]=c;
head[b]=num;
}
int main()
{
freopen("question.in","r",stdin);
freopen("question.out","w",stdout);
cin>>n;
for(ll i=1;i<=n-1;i++)
{
scanf("%d%d%d",&x,&y,&z);
add1(x,y,z);
dg[x]++;
for(ll j=1;j<=3;j++)
{
if(z>fl[x][j])
{
for(ll k=3;k>j;k--)
{
fl[x][k]=fl[x][k-1];
ff[x][k]=ff[x][k-1];
}
fl[x][j]=z;ff[x][j]=y;break;
}
}
dg[y]++;
for(ll j=1;j<=3;j++)
{
if(z>fl[y][j])
{
for(ll k=3;k>j;k--)
{
fl[y][k]=fl[y][k-1];
ff[y][k]=ff[y][k-1];
}
fl[y][j]=z;ff[y][j]=x;break;
}
}
}
for(ll i=1;i<=n;i++)
{
if(dg[i]>=3)
{
ll qwq=dg[i]-1;
qwq=(qwq*(qwq-1))/2;
for(int j=head[i];j!=0;j=next[j])
{
int p=to[j];
if(dg[p]>=2)
{
ll fas;
fas=qwq*(dg[p]-1);
ans+=fas;
sum=0;flagg=0;
for(ll k=1;k<=3;k++)
{
if(ff[i][k]!=p)
{
sum+=fl[i][k];
flagg++;
if(flagg==2) break;
}
}
sum+=v[j];
for(ll k=1;k<=3;k++)
{
if(ff[p][k]!=i)
{
sum+=fl[p][k];
break;
}
}
ans1=max(sum,ans1);
}
}
}
}
cout<<ans<<endl;
cout<<ans1;
fclose(stdin);fclose(stdout);
return 0;
}