本来是一道最小生成树的问题,但是因为边数过多,因此考虑优化
观察到这是区间信息,可以联想到使用倍增算法,将区间变成2进制,从大长度不断往下,每一层做一下最小生成树,目的是排除一些不需要的连边。用队列保存有用的
这样到0的时候做的最小生成树就是答案。这样的复杂度保证是,我们只会做log层,并且每层的队列中的信息最多是n,因此是nlogn
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=3e5+10; const int mod=1e9+7; int p[N][31]; int f[N][31]; ll sum[N],tot[N]; int n,m; struct node{ int a,b,k,w; }s[N]; queue<node> q1[30],q2[30]; bool cmp(node a,node b){ return a.w<b.w; } int find(int x,int i){ if(p[x][i]!=x){ p[x][i]=find(p[x][i],i); } return p[x][i]; } int main(){ ios::sync_with_stdio(false); cin>>n>>m; int i,j; int pp=log2(n)+1; f[1][0]=1; for(i=2;i<=n;i++){ int x; cin>>x; f[i][0]=x; } for(i=1;i<=pp;i++){ for(j=1;j<=n;j++){ f[j][i]=f[f[j][i-1]][i-1]; } } for(i=1;i<=n;i++){ for(j=0;j<=30;j++){ p[i][j]=i; } } for(i=1;i<=n;i++){ tot[i]=1; sum[i]=0; } for(i=1;i<=m;i++){ cin>>s[i].a>>s[i].b>>s[i].k>>s[i].w; } sort(s+1,s+1+m,cmp); for(i=1;i<=m;i++){ int cnt=s[i].k; node tmp=s[i]; while(cnt>0){ int pos=floor(log2(1.0*cnt)); q1[pos].push(tmp); tmp.a=f[tmp.a][pos]; tmp.b=f[tmp.b][pos]; cnt-=(1<<pos); } } for(i=pp;i>=0;i--){ while(!q1[i].empty()||!q2[i].empty()){ node tmp; if(q1[i].size()&&q2[i].size()){ auto tmp1=q1[i].front(); auto tmp2=q2[i].front(); if(tmp1.w<=tmp2.w){ tmp=tmp1; q1[i].pop(); } else{ tmp=tmp2; q2[i].pop(); } } else if(q1[i].size()){ auto tmp1=q1[i].front(); q1[i].pop(); tmp=tmp1; } else{ tmp=q2[i].front(); q2[i].pop(); } int pa=find(tmp.a,i),pb=find(tmp.b,i); if(pa!=pb){ if(i==0){ tot[pb]+=tot[pa]; p[pa][0]=pb; sum[pb]+=sum[pa]+tmp.w; } else{ p[pa][i]=pb; q2[i-1].push(tmp); tmp.a=f[tmp.a][i-1]; tmp.b=f[tmp.b][i-1]; q2[i-1].push(tmp); } } } } ll ans=1e18; int mx=0; for(i=1;i<=n;i++){ if(p[i][0]==i){ if(tot[i]>mx){ mx=tot[i]; ans=sum[i]; } else if(tot[i]==mx){ ans=min(ans,sum[i]); } } } cout<<mx<<" "<<ans<<endl; }