首先确定将所有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. 1020 Tree Traversals (25 分)(二叉树的遍历)

    给出一个棵二叉树的后序遍历和中序遍历,求二叉树的层序遍历 #include<bits/stdc++.h> using namespace std; ; int in[N]; int pos ...

  2. 编译caffe遇到的问题

    1. failed to see hdf5.h https://askubuntu.com/questions/629654/building-caffe-failed-to-see-hdf5-h 2 ...

  3. android自定义SlideMenu

                                                                   完美解决ListView中子项焦点不可被Touch的BUG. 1.在Ecl ...

  4. Nginx主要模块常用指令说明

    核心模块(Core Modules): 主模块(Main Module):配置和服务器全局有关的一些参数,比如错误日志.进程.权限等 user worker_processes error_logsy ...

  5. objective-c runtime 开发详情

    目录 概述 对象与类的实质 id与class 继承关系与isa 总结 C函数创建一个OC类 OC类与runtime NSObjectProtocol NSObject NSProxy 一.概述 Obj ...

  6. 【bzoj2969】矩形粉刷 期望

    题目描述 为了庆祝新的一年到来,小M决定要粉刷一个大木板.大木板实际上是一个W*H的方阵.小M得到了一个神奇的工具,这个工具只需要指定方阵中两个格子,就可以把这两格子为对角的,平行于木板边界的一个子矩 ...

  7. [AT2698] Don't Be a Subsequence

    题目大意:给定一个字符串,求一个最短的串要求没有在该字符串的子串中出现过,如果有多个,输出字典序最小的那一个. 题解:倒着跑一遍原字符串(以下编号为$1\sim n$),按出现了所有$26$个字母来分 ...

  8. spring in action 学习笔记三:对spring 容器的理解,以及如何利用AnnotationConfigApplicationContext这个容器创建对象

    一:spring的容器就是bean所居住的地点,这个居民点有很多的bean,有外来的bean(相当于创建了一个bean),有出去谋生的(相当于消亡了一个bean),他们之间都有某种联系 (bean与b ...

  9. 如何用cookie保存用户的登录的密码和用户名

    思路:绘制一个简单的登录界面的Servlet并要在此页面中读取保存密码和用户名的cookie--->在登录处理界面的servlet中把用户名和密码保存到cookie中 //登录界面的Servle ...

  10. mac 安装 visual studio 配置

    前言:今天主要分享的是 Mac 下一款编程软件--Visual Studio,的确,这款软件之前一直是只有 Windows 系统独占的,2017年终于开发了 Mac 版本. 微软这次为 Mac 开发者 ...