• CF468D Tree


    题意:给你一棵树,每个节点有编号1~n。求一个字典序最小的排列满足sigma(i到p[i]的距离)最大。

    n<=1e5.

    标程:

     1 #include<bits/stdc++.h>
     2 #define P pair<int,int>
     3 #define fir first
     4 #define sec second
     5 using namespace std;
     6 typedef long long ll;
     7 const int N=100005;
     8 int cnt,head[N],Max[N],size[N],rt,Ans[N],num[N],n,u,v,w,blo,anc[N];
     9 ll ans,dis[N];
    10 set<int> s[N];
    11 set<P> SZ,T;
    12 struct node{int to,next,w;}Num[N*2];
    13 void add(int x,int y,int w)
    14 {Num[++cnt].to=y;Num[cnt].next=head[x];Num[cnt].w=w;head[x]=cnt;}
    15 void find_rt(int x,int fa)
    16 {
    17     size[x]=1;
    18     for (int i=head[x];i;i=Num[i].next)
    19       if (Num[i].to!=fa)
    20       {
    21              find_rt(Num[i].to,x);
    22              size[x]+=size[Num[i].to];
    23              Max[x]=max(Max[x],size[Num[i].to]);
    24       }
    25     Max[x]=max(Max[x],n-size[x]);
    26     if (Max[rt]>Max[x]) rt=x;
    27 }
    28 void dfs(int x,int fa,int Anc)
    29 {
    30     if (Anc) anc[x]=Anc,s[Anc].insert(x);ans+=2*dis[x];
    31     for (int i=head[x];i;i=Num[i].next)
    32       if (Num[i].to!=fa)
    33       {
    34             dis[Num[i].to]=dis[x]+Num[i].w;
    35             if (!Anc) dfs(Num[i].to,x,++blo);else dfs(Num[i].to,x,Anc);
    36       }
    37 }
    38 void link(int x,int y)
    39 {
    40     int p=*s[y].begin();
    41     SZ.erase(P(num[y],y));SZ.erase(P(num[anc[x]],anc[x]));
    42     T.erase(P(p,y));
    43     Ans[x]=p;s[y].erase(p);
    44     if ((int)s[y].size()) T.insert(P(*s[y].begin(),y));
    45     num[anc[x]]--;num[y]--;
    46     SZ.insert(P(num[y],y));SZ.insert(P(num[anc[x]],anc[x]));
    47 }
    48 int main()
    49 {
    50     scanf("%d",&n);
    51     for (int i=1;i<n;i++) scanf("%d%d%d",&u,&v,&w),add(u,v,w),add(v,u,w);
    52     Max[rt=0]=n+1;find_rt(1,-1);
    53     dfs(rt,0,0);
    54     s[0].insert(rt);
    55     for (int i=0;i<=blo;i++) 
    56       T.insert(P(*s[i].begin(),i)),num[i]=2*(int)s[i].size(),SZ.insert(P(num[i],i));
    57     for (int i=1;i<=n;i++)//顺次枚举要匹配的点
    58     {
    59         if (SZ.rbegin()->fir==n-i+1&&SZ.rbegin()->sec!=anc[i]&&SZ.rbegin()->sec!=0) link(i,SZ.rbegin()->sec);
    60         else {
    61             set<P>::iterator now=T.begin();
    62             if (i!=rt&&now->sec==anc[i]) ++now;
    63             link(i,now->sec);
    64         }
    65     } 
    66     printf("%lld
    ",ans);
    67     for (int i=1;i<=n;i++) printf("%d%c",Ans[i],(i==n)?10:32);
    68     return 0;
    69 }

    易错点:1.要开ll。

    2.SZ.rbegin()->sec!=0这句话不加得出的解也是对的,但CF上说错。。。

    题解:树的重心+set+技巧

    Ans=sigma(dis[i]+dis[p[i]]-2dis[lca(i,p[i])])=2*sigma(dis[i])-2*sigma(dis[lca(i,p[i])])。

    即要使得sigma(dis[lca(i,p[i])])最小。如果lca(i,p[i])都是根答案就是2*sigma(dis[i])。

    以重心为根,肯定能够构造出解。

    考虑字典序最小。顺次枚举要匹配的i,如果直接找不在同一棵子树中的最小编号匹配,最后有可能出现不得不在同一棵树里的情况。

    统计从根裂开的每一棵子树中 编号>=i的点的个数+未被匹配的点的个数=num。每次最多会从num中取走一个点(取走两个就在同一棵子树中了)。当num=n-i+1时,则每一次必然都在该子树中选择一个。则该子树必选。反之,挑选除i所在子树外最小的一个。

    s[i]表示第i棵子树的未匹配点集。T表示每棵子树中选出的最小编号点集。SZ表示每棵子树num的集合,动态维护最大值。

  • 相关阅读:
    101与金根回顾敏捷个人:(93)《做最好的自己》之成功同心圆
    ArchiMate - 发布【企业架构语言ArchiMate v0.5.pdf】
    blog推荐 - 软件产品管理之Tyner Blain
    家庭创意:春节寻宝习俗
    流程 - 发布【敏捷方法之Scrum v0.2.pdf】
    个人管理 - 使用Scrum来敏捷自己
    blog推荐 - 电子图书与IT文档资料(ITPUB论坛)
    架构语言ArchiMate -应用层(Application Layer)
    架构语言ArchiMate - ArchiMate提供的基本视角(Viewpoints)介绍一
    故事:两只老虎的悲惨结局
  • 原文地址:https://www.cnblogs.com/Scx117/p/9072761.html
Copyright © 2020-2023  润新知