zoukankan      html  css  js  c++  java
  • 【AtCoder3611】Tree MST

    题面

    https://www.luogu.org/problem/AT3611

    题解

    点分,找到每个子树的拉格朗日点,然后把子树内所有的点向拉格朗日点连边。

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<vector>
    #define N 200050
    #define INF 1000000007LL
    #define ri register int
    using namespace std;
    int n,m,w[N];
    int siz[N],vis[N];
    int f[N];
    vector<int> to[N],len[N];
    
    struct edge{
      int u,v;
      long long l;
      bool operator < (const edge &rhs) const {
        return l<rhs.l;
      }
    } e[N*50];
    
    int findrt(int x) {
      if (f[x]==x) return x;
      return f[x]=findrt(f[x]);
    }
    
    void findroot(int x,int ff,int &rt,int &rts,int tot) {
      int curs=0;
      siz[x]=1;
      for (ri i=0,l=to[x].size();i<l;i++) if (to[x][i]!=ff && !vis[to[x][i]]) {
        findroot(to[x][i],x,rt,rts,tot);
        siz[x]+=siz[to[x][i]];
        if (siz[to[x][i]]>curs) curs=siz[to[x][i]];
      }
      if (tot-siz[x]>curs) curs=tot-siz[x];
      if (curs<rts) rts=curs,rt=x;
    }
    
    void findP(int x,int ff,int &P,long long &pd,long long pred) {
      if (pred+w[x]<pd) pd=pred+w[x],P=x;
      for (ri i=0,l=to[x].size();i<l;i++) if (to[x][i]!=ff && !vis[to[x][i]]) {
        findP(to[x][i],x,P,pd,pred+len[x][i]);
      }
    }
    
    void link(int x,int ff,int P,long long pred) {
      e[++m]=(edge){x,P,pred+w[x]};
      for (ri i=0,l=to[x].size();i<l;i++) if (to[x][i]!=ff && !vis[to[x][i]]) {
        link(to[x][i],x,P,pred+len[x][i]);
      }
    }
    
    void tonji(int x,int ff,int &tot){
      tot++;
      for (ri i=0,l=to[x].size();i<l;i++) if (to[x][i]!=ff && !vis[to[x][i]]) tonji(to[x][i],x,tot);
    }
    
    void solve(int x){
      vis[x]=1;
      int p=-1;
      long long pd=INF*INF;
      findP(x,-1,p,pd,0LL);
      link(x,-1,p,pd);
      for (ri i=0,l=to[x].size();i<l;i++) if (!vis[to[x][i]]) {
        int tot=0;
        tonji(to[x][i],x,tot);
        int rt=-1,rts=tot;
        findroot(to[x][i],x,rt,rts,tot);
        solve(rt);
      }
    }
    
    int main() {
      int a,b,c;
      scanf("%d",&n);
      for (ri i=1;i<=n;i++) scanf("%d",&w[i]);
      for (ri i=1;i<n;i++) {
        scanf("%d %d %d",&a,&b,&c);
        to[a].push_back(b); len[a].push_back(c);
        to[b].push_back(a); len[b].push_back(c);
      };
      int rt=-1,rts=n;
      findroot(1,-1,rt,rts,n);
      solve(rt);
      sort(e+1,e+m+1);
      long long ans=0;
      for (ri i=1;i<=n;i++) f[i]=i;
      for (ri i=1;i<=m;i++) {
        int r1=findrt(e[i].u),r2=findrt(e[i].v);
        if (r1==r2) continue;
        ans+=e[i].l;
        f[r1]=r2;
      }
      cout<<ans<<endl;
      return 0;
    }
  • 相关阅读:
    查询反模式
    查询反模式
    查询反模式
    查询反模式
    linux vi(vim)常用命令汇总(转)
    面试笔试题之二叉树经典25题
    查找至少一个重复元素
    海盗分金问题
    Output of C++ Program | Set 18
    Output of C++ Program | Set 17
  • 原文地址:https://www.cnblogs.com/shxnb666/p/11275664.html
Copyright © 2011-2022 走看看