zoukankan      html  css  js  c++  java
  • 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))。

  • 相关阅读:
    std thread
    windows更新包发布地址
    How to set up logging level for Spark application in IntelliJ IDEA?
    spark 错误 How to set heap size in spark within the Eclipse environment?
    hadoop 常用命令
    windows 安装hadoop 3.2.1
    windows JAVA_HOME 路径有空格,执行软连接
    day01MyBatisPlus条件构造器(04)
    day01MyBatisPlus的CRUD 接口(03)
    day01MyBatisPlus入门(02)
  • 原文地址:https://www.cnblogs.com/Scx117/p/8876794.html
Copyright © 2011-2022 走看看