首先确定将所有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类的继承

    孤荷凌寒自学python第二十二天python类的继承 (完整学习过程屏幕记录视频地址在文末,手写笔记在文末) python中定义的类可以继承自其它类,所谓继承的概念,我的理解 是,就是一个类B继承自 ...

  2. gulp入门1

    1. 下载.安装git(https://git-scm.com/downloads),学会使用命令行. 2. 下载.安装node.js(https://nodejs.org/en/),现在node.j ...

  3. 基于eclipse+maven创建web工程

    Eclipse+Maven创建webapp项目<一> 1.开启eclipse,右键new——>other,如下图找到maven project 2.选择maven project,显 ...

  4. Python中的多线程编程,线程安全与锁(二)

    在我的上篇博文Python中的多线程编程,线程安全与锁(一)中,我们熟悉了多线程编程与线程安全相关重要概念, Threading.Lock实现互斥锁的简单示例,两种死锁(迭代死锁和互相等待死锁)情况及 ...

  5. MapReduce架构

    主从结构 主节点:JobTracker(一个) 从节点:TaskTrackers(多个) JobTracker: 接收客户提交的计算任务 把计算任务分配给TaskTrackers执行 监控TaskTr ...

  6. 【Luogu】P2498拯救小云公主(spfa)

    题目链接 我水爆了- - 容易发现可以把两个圆之间连边,左上为起点右下为终点,最小生成树直到起点跟终点连起来,输出边权/2就行. 然后80. 并不理解为什么这可以转化成spfa求最短路,邻接矩阵暴力跑 ...

  7. POJ 2115 C-Looooops | exgcd

    题目 给出一个循环for(int i=A;i!=B;i+=C) 在mod (1<<k) 下是否可以退出循环 是,输出时间,否输出FORVEER 题解: 题意可以变换成 A+Cx=B (mo ...

  8. div样式

    DIV样式汇总 一.常用属性: 1.Height:设置DIV的高度. 2.Width:设置DIV的宽度. 例: <div style="width:200px;height:200px ...

  9. 朗格拉日计数(counter)

    朗格拉日计数(counter) 题目描述 在平面上以圆周等分排列着n个带标号(标号为1-n)的点,你需要计算有多少个三元组(a,b,c),满足a<b<c而且标号为a,b,c的点在圆上分布的 ...

  10. ZJUTACM

    描述 这回是浙江工业大学的ACM程序设计竞赛,欢迎你的到来!但是,请稍等!裁判Joe说了,必须正确回答他的问题,才可以看到PIPI的气球MM,KUKU的气球GG.Joe手上有7张卡片,每张卡片上有一个 ...