首先确定将所有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. (原)Unreal渲染相关的缓冲区 及其 自定义代码几种抓取

    @authot: 白袍小道   转载说明那啥即可. (图片和本文无关,嘿嘿,坑一下)         以下为Unreal4.18版本中对GPUBuffer部分的分析结果 (插入:比之够着,知至目的) ...

  2. [转载]kd tree

    [本文转自]http://www.cnblogs.com/eyeszjwang/articles/2429382.html k-d树(k-dimensional树的简称),是一种分割k维数据空间的数据 ...

  3. docker 生成新的镜像

    下载了ubuntu的初始化镜像,但是没有网络安装包,安装了字后,如果生成新的镜像 sudo docker commit -m "add ifconfig/ping package" ...

  4. linux进程——后台运行的方法

    linux进程后台运行的几种方法: 我们经常会碰到这样的问题,用 telnet/ssh 登录了远程的 Linux 服务器,运行了一些耗时较长的任务, 结果却由于网络的不稳定导致任务中途失败. 如何让命 ...

  5. [ CodeVS冲杯之路 ] P3116

    不充钱,你怎么AC? 题目:http://codevs.cn/problem/3116/ 基础的高精度加法,注意一下两个数长短不一和答案第一位的处理即可,当然也可以用压位的方法做 #include&l ...

  6. Python数据结构——栈

    栈是一种特殊的列表,栈内的元素只能通过列表的一端访问,这一端称为栈顶.栈被称为一种后入先出(LIFO,last-in-first-out)的数据结构. 由于栈具有后入先出的特点,所以任何不在栈顶的元素 ...

  7. RUNAS UAC

    cookielib pip install wmi _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE', 0, _winreg.KEY_ALL ...

  8. gluPerspective(解释得很好)

    http://www.cppblog.com/COOOOOOOOL/archive/2009/12/28/104255.html 函数原型gluPerspective(GLdouble fovy,GL ...

  9. linux内核情景分析之exit与Wait

    //第一层系统调用 asmlinkage long sys_exit(int error_code) { do_exit((error_code&0xff)<<8); } 其主体是 ...

  10. linux下如何实现简单刷屏【转】

    转自:http://blog.csdn.net/figthter_cui/article/details/6145272 版权声明:本文为博主原创文章,未经博主允许不得转载. 今天为了测试我的LCD有 ...