NC14419 线路规划(最小生成树+倍增)

本来是一道最小生成树的问题,但是因为边数过多,因此考虑优化

观察到这是区间信息,可以联想到使用倍增算法,将区间变成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;
}
View Code
没有人不辛苦,只有人不喊疼
原文地址:https://www.cnblogs.com/ctyakwf/p/13971401.html