首先确定将所有a[i]向i连边之后会形成一张图,图上每条有向边i->j表示i要在j之前选。

图上的每个拓扑序都对应一种方案(如果有环显然无解),经过一系列推导可以发现贪心策略与合并的块的大小和w之和有关,具体见https://kelin.blog.luogu.org/solution-p4437

贪心的时候每次要选w平均值最大的,这个可以用STL维护,具体使用哪种见下。

一:STL-priority_queue

最简单直接的做法,每次更新的时候直接加入即可,后面弹出的时候判一下这个点是否已经被更新即可。

BZOJ上AC,Luogu上开O2能A,不开会RE两个点。

 #include<cstdio>
#include<queue>
#include<algorithm>
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
typedef long long ll;
using namespace std; const int N=;
int n,u,p,fa[N],tim,cnt,vis[N],f[N],sz[N],h[N],nxt[N<<],to[N<<];
ll ans,w[N];
struct D{ int u,sz; ll w; bool operator <(const D &b)const{ return w*b.sz>b.w*sz; } };
priority_queue<D>Q; void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; } void dfs(int u){
vis[u]=; tim++;
for (int i=h[u],v; i; i=nxt[i])
if (vis[v=to[i]]) { puts("-1"); exit(); } else dfs(v);
} int get(int x){ return (f[x]==x) ? x : f[x]=get(f[x]); } int main(){
freopen("bzoj5289.in","r",stdin);
freopen("bzoj5289.out","w",stdout);
scanf("%d",&n);
rep(i,,n) scanf("%d",&fa[i]),add(fa[i],i);
dfs(); if (tim<=n) { puts("-1"); return ; }
rep(i,,n) f[i]=i,sz[i]=;
rep(i,,n) scanf("%lld",&w[i]),Q.push((D){i,,w[i]});
while (!Q.empty()){
D s=Q.top(); Q.pop();
if (sz[u=get(s.u)]!=s.sz) continue;
f[u]=p=get(fa[u]); ans+=w[u]*sz[p];
w[p]+=w[u]; sz[p]+=sz[u];
if (p) Q.push((D){p,sz[p],w[p]});
}
printf("%lld\n",ans);
return ;
}

二:STL-set

我也不知道上面的方法为什么会RE,然后换成set就不存在这个问题了,取而代之的是超大常数。。

BZOJ上AC,Luogu上开O2能A,不开会TLE两个点。

 #include<cstdio>
#include<set>
#include<algorithm>
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
typedef long long ll;
using namespace std; const int N=;
int n,u,p,fa[N],tim,cnt,vis[N],f[N],sz[N],h[N],nxt[N<<],to[N<<];
ll ans,w[N];
struct D{
int u,sz; ll w;
bool operator <(const D &b)const{ return (w*b.sz!=b.w*sz) ? w*b.sz<b.w*sz : ((u!=b.u)?u<b.u:(sz<b.sz)); }
};
multiset<D>Q; void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; } void dfs(int u){
vis[u]=; tim++;
for (int i=h[u],v; i; i=nxt[i])
if (vis[v=to[i]]) { puts("-1"); exit(); } else dfs(v);
} int get(int x){ return (f[x]==x) ? x : f[x]=get(f[x]); } int main(){
freopen("bzoj5289.in","r",stdin);
freopen("bzoj5289.out","w",stdout);
scanf("%d",&n);
rep(i,,n) scanf("%d",&fa[i]),add(fa[i],i);
dfs(); if (tim<=n) { puts("-1"); return ; }
rep(i,,n) f[i]=i,sz[i]=;
rep(i,,n) scanf("%lld",&w[i]),Q.insert((D){i,,w[i]});
while (!Q.empty()){
D s=*Q.begin(); Q.erase(s);
if (sz[u=get(s.u)]!=s.sz) continue;
f[u]=p=get(fa[u]); ans+=w[u]*sz[p];
w[p]+=w[u]; sz[p]+=sz[u];
if (p) Q.insert((D){p,sz[p],w[p]});
}
printf("%lld\n",ans);
return ;
}

三:__gnu_pbds::priority_queue

pb_ds库里的堆天生支持修改,但一般常数将是STL的接近三倍。

但是这题并没有体现,视平台不同而有所差异,BZOJ上和set同速,本机甚至比set和STL-priority_queue都快。

BZOJ上AC,Luogu上AC。

 #include<cstdio>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/priority_queue.hpp>
#include<algorithm>
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
typedef long long ll;
using namespace std; const int N=;
int n,u,p,fa[N],tim,cnt,vis[N],f[N],sz[N],h[N],nxt[N<<],to[N<<];
ll ans,w[N];
struct D{ int u; ll w; };
struct Cmp{
bool operator()(const D &a,const D &b)const
{ return (a.w*sz[b.u]!=b.w*sz[a.u]) ? a.w*sz[b.u]>b.w*sz[a.u] : ((a.u!=b.u)?a.u>b.u:(sz[a.u]>sz[b.u])); }
};
__gnu_pbds::priority_queue<D,Cmp>Q;
__gnu_pbds::priority_queue<D,Cmp>::point_iterator its[N]; void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; } void dfs(int u){
vis[u]=; tim++;
for (int i=h[u],v; i; i=nxt[i])
if (vis[v=to[i]]) { puts("-1"); exit(); } else dfs(v);
} int get(int x){ return (f[x]==x) ? x : f[x]=get(f[x]); } int main(){
freopen("bzoj5289.in","r",stdin);
freopen("bzoj5289.out","w",stdout);
scanf("%d",&n);
rep(i,,n) scanf("%d",&fa[i]),add(fa[i],i);
dfs(); if (tim<=n) { puts("-1"); return ; }
rep(i,,n) f[i]=i,sz[i]=;
rep(i,,n) scanf("%lld",&w[i]),its[i]=Q.push((D){i,w[i]});
while (!Q.empty()){
int u=Q.top().u; Q.pop();
f[u]=p=get(fa[u]); ans+=w[u]*sz[p];
w[p]+=w[u]; sz[p]+=sz[u];
if (p) Q.modify(its[p],(D){p,w[p]});
}
printf("%lld\n",ans);
return ;
}

[BZOJ5289][HNOI2018]排列(拓扑排序+pb_ds)的更多相关文章

  1. [HNOI2018]游戏[拓扑排序]

    题意 题目链接 分析 先将没有锁的房间缩点,首先有一个 \(O(n^2)\) 的想法:从每个点出发,每次检查能否向两边扩张. 容易发现门和门之间如果有锁,必然只有一方能够开锁(只有一把钥匙),并且能够 ...

  2. BZOJ5289: [Hnoi2018]排列

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

  3. [BZOJ5288][HNOI2018]游戏(拓扑排序)

    传送门:https://www.luogu.org/problemnew/show/P4436 20分的暴力加一个Random_shuffle就A了.我还能说什么.. 不过这个也不是毫无道理,复杂度应 ...

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

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

  5. 【BZOJ5288】[HNOI2018]游戏(拓扑排序)

    [BZOJ5288][HNOI2018]游戏(拓扑排序) 题面 BZOJ 洛谷 题解 去年省选的时候这题给我乱搞整过去整过去了,也是虐心了.... 所以当然是来讲正儿八经的正确做法啦. 很明显,我们需 ...

  6. 拓扑排序+不是字典序的优先级排列(POJ3687+HDU4857)

    一.前言 在过去的一周里结束了CCSP的比赛,其中有一道题卡了我9个小时,各种调错都没法完整的调处来这题,于是痛下决心开始补题,这个是计划的一部分.事实上,基于错误的理解我写了若干发拓扑排序+字典序的 ...

  7. uoj#278. 【UTR #2】题目排列顺序(拓扑排序)

    传送门 对于每一个位置\(i\)来说,上一个和它的\(f_i\)相同的点一定比它大,我们从上一个\(f_i\)和它相同的点向它连边.第一个\(f_i-1\)出现的位置一定比它小,把它向那个位置连边. ...

  8. 有向无环图的应用—AOV网 和 拓扑排序

    有向无环图:无环的有向图,简称 DAG (Directed Acycline Graph) 图. 一个有向图的生成树是一个有向树,一个非连通有向图的若干强连通分量生成若干有向树,这些有向数形成生成森林 ...

  9. ACM/ICPC 之 拓扑排序-反向(POJ3687)

    难点依旧是题意....需要反向构图+去重+看题 POJ3687-Labeling Balls 题意:1-N编号的球,输出满足给定约束的按原编号排列的重量序列,如果有多组答案,则输出编号最小的Ball重 ...

随机推荐

  1. 【Python】python内置函数、列表生成式、生成器

    一.内置函数 1 print(all([1,2,3,4]))#判断可迭代的对象里面的值是否都为真 2 print(any([0,1,2,3,4]))#判断可迭代的对象里面的值是否有一个为真 3 pri ...

  2. 孤荷凌寒自学python第四十二天python线程控制之Condition对象

     孤荷凌寒自学python第四十二天python的线程同步之Condition对象 (完整学习过程屏幕记录视频地址在文末,手写笔记在文末) 今天学习了Condition对象,发现它综合了Event对象 ...

  3. [OpeCV] highgui头文件

    通过HighGUI(high-level graphical user interface)可以实现打开窗口.显示图像.读出和写入图像文件,处理鼠标.光标.键盘事件. 而HighGUI主要分成“视频输 ...

  4. 课时46:魔法方法:描述符(property的原理)

    目录: 一.描述符(property的原理) 二.课时46课后习题及答案 ********************************** 一.描述符(property的原理) ********* ...

  5. leetcode_day01

    任务一:有效的括号 题目链接:https://leetcode-cn.com/problems/valid-parentheses/ 自己的答案: class Solution: def isVali ...

  6. vue 三目运算

    :class="followed ? 'btn-success':'btn-secondary'"

  7. 实验 使用 vivado zedboard GPIO 开关 开控制 LED

    前面我做了几个实验 都没有用过 开关,这一次用一用 发现 vivado 真的挺方便 所以 使用 vivado 开发 1.建工程 我使用 vivado 2013.4 创建新工程 –> next – ...

  8. PAT 1075 链表元素分类

    https://pintia.cn/problem-sets/994805260223102976/problems/994805262953594880 给定一个单链表,请编写程序将链表元素进行分类 ...

  9. jquery radio 行选中 操作

    想实现点击一行中任意位置 此行的 radio 选中. function rowClick(t) { var id = $(t).attr("id").substr(3, 1); / ...

  10. Arcgis桌面开发,Python引用GDAL库的方法

    我用的是arcgis10.2,python版本是arcgis自动安装的Pythin2.7 1.下载gdal-111-1700-core.msi和对应的GDAL-1.11.1.win32-py2.7.m ...