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

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

标程:

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll read()
{
ll x=,f=;char ch=getchar();
while (ch<''||ch>'') {if (ch=='-') f=-;ch=getchar();}
while (ch>=''&&ch<='') x=(x<<)+(x<<)+ch-'',ch=getchar();
return x*f;
}
const int N=;
vector<ll> vec[N];
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];
int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
struct _node{ll sum,he,sz,id;_node(ll A,ll B,ll C,ll D){sum=A;he=B;sz=C;id=D;}};
struct cmp{
bool operator () (const _node &A,const _node &B)
{return A.he*B.sz>B.he*A.sz||A.he*B.sz==B.he*A.sz&&A.sz<B.sz;}
};
priority_queue<_node,vector<_node>,cmp> q;
int main()
{
n=read();
for (int i=;i<=n;i++) f[i]=i;
for (int i=;i<=n;i++)
{
a[i]=read();
if (<a[i]&&a[i]<=n) vec[a[i]].push_back(i),fa[i]=a[i]; else fa[i]=-;
}
for (int i=;i<=n;i++) w[i]=read(),q.push(_node(sum[i]=w[i],he[i]=w[i],sz[i]=,i));
while (!q.empty())
{
int x=q.top().id,fx=fa[find(x)];q.pop();
if (vis[x]) continue; vis[x]=; //dijkstra的思想,肯定先访问最后一次合并过的点,其他过去版本直接continue,这样就不用再记录一个del的堆。
if (fx==-)
{
ans+=sum[x]+cn*he[x];
for (int i=;i<vec[x].size();i++) fa[vec[x][i]]=-;
cn+=sz[x];
}
else {
if (vis[fx]) return puts("-1"),;
for (int i=;i<vec[x].size();i++) f[vec[x][i]]=find(x);
sum[fx]+=sum[x]+he[x]*sz[fx];he[fx]+=he[x];sz[fx]+=sz[x];
q.push(_node(sum[fx],he[fx],sz[fx],fx));
}
}
printf("%lld\n",ans);
return ;
}

易错点: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))。

loj2509 hnoi2018排列的更多相关文章

  1. 【BZOJ5289】[HNOI2018]排列(贪心)

    [BZOJ5289][HNOI2018]排列(贪心) 题面 BZOJ 洛谷 题解 这个限制看起来不知道在干什么,其实就是找到所有排列\(p\)中,\(p_k=x\),那么\(k<j\),其中\( ...

  2. 5289: [Hnoi2018]排列

    5289: [Hnoi2018]排列 链接 分析: 首先将题意转化一下:每个点向a[i]连一条边,构成了一个以0为根节点的树,要求选一个拓扑序,点x是拓扑序中的第i个,那么价值是i*w[x].让价值最 ...

  3. bzoj 5289: [Hnoi2018]排列

    Description Solution 首先注意到实际上约束关系构成了一棵树 考虑这个排列 \(p\),编号为 \(a[i]\) 的出现了,\(i\) 才可以出现 那么如果连边 \((a[i],i) ...

  4. [HNOI2018]排列

    Description: 给定 \(n\) 个整数 \(a_1, a_2, \dots, a_n, 0 \le a_i \le n\),以及 \(n\) 个整数 \(w_1, w_2, \dots, ...

  5. [HNOI2018]排列[堆]

    题意 给定一棵树,每个点有点权,第 \(i\) 个点被删除的代价为 \(w_{p[i]}\times i\) ,问最小代价是多少. 分析 与国王游戏一题类似. 容易发现权值最小的点在其父亲选择后就会立 ...

  6. BZOJ5289: [Hnoi2018]排列

    传送门 第一步转化,令 \(q[p[i]]=i\),那么题目变成: 有一些 \(q[a[i]]<q[i]\) 的限制,\(q\) 必须为排列,求 \(max(\sum_{i=1}^{n}w[i] ...

  7. BZOJ.5289.[AHOI/HNOI2018]排列(贪心 heap)

    BZOJ LOJ 洛谷 \(Kelin\)写的挺清楚的... 要求如果\(a_{p_j}=p_k\),\(k\lt j\),可以理解为\(k\)要在\(j\)之前选. 那么对于给定的\(a_j=k\) ...

  8. 【比赛】HNOI2018 排列

    这题原题... 这题题面七绕八绕,有点麻烦,反正最后转化就是一棵树,每个点有一个值,要把所有点选完,要求选择一个点必须是它的父亲和祖先已经全部被选了,贡献是这个点的权值乘上它被选择的排名 如果一个点是 ...

  9. [BZOJ5289][HNOI2018]排列(拓扑排序+pb_ds)

    首先确定将所有a[i]向i连边之后会形成一张图,图上每条有向边i->j表示i要在j之前选. 图上的每个拓扑序都对应一种方案(如果有环显然无解),经过一系列推导可以发现贪心策略与合并的块的大小和w ...

随机推荐

  1. 扩展BSGS-传送门

    很好的讲解:ZigZagK 好的讲解:mjtcn 某个模板:here 模板题: BSGS:ZigZagK的poj2417 exBSGS:ZigZagK的poj3243 — AC_Gibson 一般的板 ...

  2. Linux date命令 crontab每个月最后一天

    ###使用date获取日期时间等 # 当前日期 openstack@ubuntu:~$ date 2019年 01月 15日 星期二 15:10:49 CST # 明天 openstack@ubunt ...

  3. C++之静态(static)

    一.静态数据成员与静态成员函数 二.从内存角度看静态数据成员 三.从this指针谈静态成员函数 四.注意事项 五.补充说明 1.<静态>课程评论: 静态成员是类的成员,不是对象的成员: 静 ...

  4. Mybatis笔记 - Mybatis框架简介

    MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis,实质上 ...

  5. VS新建工程或者新建项时 出现未定义标识符号

    VS新建工程或者新建项时 出现未定义标识符号,编译之后不影响运行,但是看着很不舒服,影响效率. 解决办法:属性--->VC++目录-->包含目录-->编辑,将自己所用QT的inclu ...

  6. 【牛客挑战赛32E】树上逆序对

    题目 数据范围非常奇怪,询问的逆序对个数\(k\leq 30000\),我们应该可以把所有的情况都求出来 发现对于树上两点\(x,y\),如果\(x\)是\(y\)的祖先,那么绝对值较大的点的符号决定 ...

  7. PE头里的东西更多。。。越看越恶心了,我都不想看了

    winnt.h 中,定义的PE头结构体 typedef struct _IMAGE_NT_HEADERS{DWORD Signature;//PE文件头标志:PE\0\0.在开始DOS header的 ...

  8. dubbo视频分享

    一.基础篇 第001节--课程介绍 第01节--使用Dubbo对传统工程进行服务化改造的思路介绍 第02节--使用Dubbo对传统工程进行服务化改造 第03节--ZooKeeper注册中心安装 第04 ...

  9. Redis的持久化和主从复制机制

    Redis持久化 Redis 提供了多种不同级别的持久化方式: RDB 持久化可以在指定的时间间隔内生成数据集的时间点快照(point-in-time snapshot) AOF 持久化记录服务器执行 ...

  10. JS对象 向下取整floor() floor() 方法可对一个数进行向下取整。 语法: Math.floor(x)

    向下取整floor() floor() 方法可对一个数进行向下取整. 语法: Math.floor(x) 参数说明: 注意:返回的是小于或等于x,并且与 x 最接近的整数. 我们将在不同的数字上使用 ...