• loj2509 hnoi2018排列


    题意:对于a数组,求它的一个合法排列的最大权值。合法排列:对于任意j,k,如果a[p[j]]=p[k],那么k<j。

    权值:sigma(a[p[i]]*i)。n<=50W。

    标程:

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 ll read()
     5 {
     6    ll x=0,f=1;char ch=getchar();
     7    while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
     8    while (ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
     9    return x*f;
    10 }
    11 const int N=500005;
    12 vector<ll> vec[N];
    13 ll ans,sum[N],he[N],cnt,head[N],n,a[N],fa[N],w[N],sz[N],cn,vis[N],tail[N],f[N];
    14 int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
    15 struct _node{ll sum,he,sz,id;_node(ll A,ll B,ll C,ll D){sum=A;he=B;sz=C;id=D;}};
    16 struct cmp{
    17     bool operator () (const _node &A,const _node &B)
    18     {return A.he*B.sz>B.he*A.sz||A.he*B.sz==B.he*A.sz&&A.sz<B.sz;}
    19 };
    20 priority_queue<_node,vector<_node>,cmp> q;
    21 int main()
    22 {
    23     n=read();
    24     for (int i=1;i<=n;i++) f[i]=i;
    25     for (int i=1;i<=n;i++) 
    26     {
    27         a[i]=read();
    28         if (0<a[i]&&a[i]<=n) vec[a[i]].push_back(i),fa[i]=a[i]; else fa[i]=-1;
    29     }
    30     for (int i=1;i<=n;i++) w[i]=read(),q.push(_node(sum[i]=w[i],he[i]=w[i],sz[i]=1,i));
    31     while (!q.empty())
    32     {
    33         int x=q.top().id,fx=fa[find(x)];q.pop();
    34         if (vis[x]) continue; vis[x]=1; //dijkstra的思想,肯定先访问最后一次合并过的点,其他过去版本直接continue,这样就不用再记录一个del的堆。
    35        if (fx==-1) 
    36        {
    37            ans+=sum[x]+cn*he[x];
    38            for (int i=0;i<vec[x].size();i++)  fa[vec[x][i]]=-1;
    39            cn+=sz[x];
    40         }
    41        else {    
    42            if (vis[fx]) return puts("-1"),0; 
    43            for (int i=0;i<vec[x].size();i++) f[vec[x][i]]=find(x);
    44            sum[fx]+=sum[x]+he[x]*sz[fx];he[fx]+=he[x];sz[fx]+=sz[x];
    45            q.push(_node(sum[fx],he[fx],sz[fx],fx));
    46         }
    47     }
    48     printf("%lld
    ",ans);
    49    return 0;
    50 }

    易错点:1.居然碰到了yhx钦定的最难调错误没有之一,记!

    return A.he*B.sz>B.he*A.sz||A.he*B.sz==B.he*A.sz&&A.sz<B.sz;
    如果不判定相等的情况就不一定取到最后一个。

    2.判断无解:vis表示已经被合并/删除的节点,重新连爸爸后爸爸应该是没有被删除的,如果vis[fa]=1,那么必然矛盾。

    3.更改父亲的操作如果用vector暴力加,时间复杂度会到O(n^2)。用并查集保存同父亲的点是最快的做法。

    题解:堆+并查集+建树+贪心

    做法好神。如果a[p[j]]=p[k],那么k<j:也就是说比如a[1]=3,那么在排列中3一定在1前面。对于1<=a[i]<=n的点,连边a[i]->i,表示先取a[i],再取i。那么就形成了一棵树,如果有环必然无解。

    这棵树肯定是每次取一个没有父亲的点作为p[i]。基于贪心,i越小,选越小的w[i]更优。

    因此我们每次用堆/set维护权值最小的点,如果它没有父亲肯定直接取走,反之和其父亲合并,表示如果取走父亲后接下来肯定就取它。

    合并之后,该点的儿子都连边向它父亲,也就是说fa[son[x]]=fa[x],可以用并查集维护。这样这个点的权值用sigma/size来代替。

    可以证明:1.比较两个点的sigma/size就相当于比较它们sigma(i*w[p[i]])的权值。

    2.对于同一个点,sigma/size随着合并不严格单调减。(设he1/sz1<he2/sz2,那么1向2合并,必然有he2/sz2>(he1+he2)/(sz1+sz2)。化简he2/sz2>(he1+he2)/(sz1+sz2),则he1*sz2<he2*sz1,同假设成立)

    时间复杂度O(nlogn+na(n))。

  • 相关阅读:
    安装curl依赖库后yum不能使用问题解决
    leetcode Container With Most Water
    leetcode Median of Two Sorted Arrays
    leetcode Add Two Numbers(对指针的一些基本操作)
    hdu 4427 DP
    hdu 4454 三分*****
    HDU5917 RAMSEY定理
    UVAlive7501 Business Cycle 2015ECfinal B(二分模板)
    已知圆半径和外接正多边形边数求边长
    hdu4799 树型DP
  • 原文地址:https://www.cnblogs.com/Scx117/p/8876794.html
Copyright © 2020-2023  润新知