洛谷 P4437 [HNOI/AHOI2018]排列(贪心+堆,思维题)
开始 WA ycx 的遗产(bushi
首先可以将题目转化为图论模型:\(\forall i\) 连边 \(a_i\to i\),然后求图的一个拓扑序 \(b_1,b_2,\dots b_n\) 使得 \(\sum\limits_{i=1}^niw_{b_i}\) 最小。显然如果原图出现环就 \(-1\) 了。否则原图一定是一棵森林。
然后我就在那儿想各种乱搞,包括但不限于 DP、贪心之类,然后都假掉了
我们知道有个东西叫排序不等式,它是说 \(\forall a_1\leq a_2\leq a_3\leq\dots\leq a_n,b_1\leq b_2\leq b_3\leq\dots\leq b_n\) 都有 \(\sum\limits_{i=1}^na_ib_i\geq\sum\limits_{i=1}^na_ib_{p_i}\geq\sum\limits_{i=1}^na_ib_{n-i+1}\),其中 \(p\) 为 \(1\sim n\) 的任意一个排列。
将排序不等式应用于此题。如果设 \(a_i=i\),\(b\) 为 \(w\) 排好序的结果可得理论最大值为 \(\sum\limits_{i=1}^nib_i\)。换句话说,假设使 \(w_i\) 取到最小值的 \(i\) 为 \(x\),那么我们肯定贪心地想把 \(x\) 放在前面,越靠前越好。
不过问题是,\(a_x\) 不一定等于 \(0\),也就是说 \(x\) 不一定有能力成为拓扑序的第一个元素。这时候该怎么办呢?我们不考虑现在,我们考虑未来。我们定义当前状态下的“可选集合”为满足 \(a_x=0\) 或者 \(a_x\) 已经被加入拓扑序的 \(x\) 的集合,也就是说可选集合内的元素都可以成为拓扑序中的下一个元素。考虑什么时候 \(x\) 能够进入可选集合。显然要当 \(a_x\) 已经被加入拓扑序之后。而根据之前的推论我们肯定希望 \(x\) 越靠前越好,也就是说一旦 \(a_x\) 被加入拓扑序,我们就令 \(x\) 为拓扑序中下一个元素,相当于将 \(a_x\) 与 \(x\) 捆绑在了一起。
P.S:如果你对为什么 \(x\) 越靠前越好的证明感兴趣的话,可以从这里往下看:
假设 \(a_x\) 是第 \(s\) 个加入拓扑序的,\(x\) 是第 \(t(t>s+1)\) 个加入拓扑序的,第 \(i\) 个加入拓扑序的元素为 \(b_{i}\),那么我们显然有代价为 \(\sum\limits_{i=1}^siw_{b_i}+\sum\limits_{i=1}^{t-s-1}(s+i)w_{b_{i+s}}+tw_x+\sum\limits_{i=t+1}^niw_{b_i}\)
而倘若我们将第 \(x\) 个元素加入拓扑序的时间提前到 \(s+1\),其他元素加入拓扑序的时间保持不变,那么代价变为 \(\sum\limits_{i=1}^siw_{b_i}+(s+1)w_x+\sum\limits_{i=1}^{t-s-1}(s+i+1)w_{b_i+s}+\sum\limits_{i=t+1}^niw_{b_i}\)
二者做差可得 \(\Delta=\sum\limits_{i=1}^{t-s-1}w_{b_i+s}-(t-s-1)w_x\)
根据 \(w_x\) 为 \(\min\{w_i\}\) 可知 \(\dfrac{\sum\limits_{i=1}^{t-s-1}w_{b_i+s}}{t-s-1}\geq w_x\),换句话说,任意 \(t-s-1\) 个 \(w_i\) 的平均值 \(\geq w_x\)。
故 \(\Delta\geq 0\)。而显然将第 \(x\) 个元素加入拓扑序的时间提前到 \(s+1\) 依旧合法。故我们选择 \(x\) 越靠前越好。
还有一个问题,就是经过若干次合并后树上的每一个元素都是一个合并序列,也就是若干个形如 \(t_1,t_2,\dots t_m\),其中 \(t_i\) 拓扑序下一个必须是 \(t_{i+1}\) 的序列。此时我们就要探究合并序列的大小关系,考虑两个序列 \(x_1,x_2,\dots x_p\) 和 \(y_1,y_2,\dots,y_q\) 合并的大小关系。
- 若 \(x\) 合并到 \(y\) 前面,\(W=\sum\limits_{i=1}^piw_{x_i}+\sum\limits_{i=1}^q(i+p)w_{y_i}\)
- 若 \(y\) 合并到 \(x\) 前面,\(W=\sum\limits_{i=1}^qiw_{y_i}+\sum\limits_{i=1}^p(i+q)w_{x_i}\)
二者做差可得 \(\Delta=-q\times\sum\limits_{i=1}^pw_{x_i}+p\times\sum\limits_{i=1}^qw_{y_i}\)
我们不妨假设 \(\Delta\geq 0\),不等号两边同除 \(pq\) 可得 \(-\dfrac{\sum\limits_{i=1}^pw_{x_i}}{p}+\dfrac{\sum\limits_{i=1}^qw_{y_i}}{q}\geq 0\)。
即 \(\dfrac{\sum\limits_{i=1}^qw_{y_i}}{q}\geq \dfrac{\sum\limits_{i=1}^pw_{x_i}}{p}\)
也就是说我们选择将平均值大的序列合并到平均值小的序列的后面。
用堆维护即可。
复杂度线性对数。
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned int u32;
typedef unsigned long long u64;
namespace fastio{
#define FILE_SIZE 1<<23
char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
inline void putc(char x){(*p3++=x);}
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=0;
while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(neg) x=(~x)+1;
}
template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
const int MAXN=5e5;
int n,a[MAXN+5],b[MAXN+5],vis[MAXN+5];
void dfs(int x){
if(!(vis[x]^2)) puts("-1"),exit(0);
if(!(vis[x]^1)) return;vis[x]=2;
if(a[x]) dfs(a[x]);vis[x]=1;
}
int f[MAXN+5],siz[MAXN+5];ll sum[MAXN+5],val[MAXN+5];
int getf(int x){return (!~f[x])?x:f[x]=getf(f[x]);}
void merge(int x,int y){
x=getf(x);y=getf(y);
f[y]=x;sum[x]+=sum[y];
val[x]+=val[y]+1ll*sum[y]*siz[x];
siz[x]+=siz[y];
// printf("merge %d %d %d %lld %lld\n",x,y,siz[x],sum[x],val[x]);
}
struct frac{
ll x,y;
frac(ll _x=0,ll _y=1):x(_x),y(_y){}
bool operator <(const frac &rhs) const{
return x*rhs.y<rhs.x*y;
}
};
set<pair<frac,int> > st;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) scanf("%d",&b[i]);
for(int i=1;i<=n;i++) dfs(i);f[0]=-1;
for(int i=1;i<=n;i++) f[i]=-1,siz[i]=1,sum[i]=val[i]=b[i];
for(int i=1;i<=n;i++) st.insert(mp(frac(b[i],1),i));
while(!st.empty()){
pair<frac,int> pp=*st.begin();st.erase(st.begin());
int id=pp.se,f1=getf(id),f2=getf(a[f1]);
if(f2) st.erase(st.find(mp(frac(sum[f2],siz[f2]),f2)));
merge(f2,f1);if(f2) st.insert(mp(frac(sum[f2],siz[f2]),f2));
} printf("%lld\n",val[0]);
return 0;
}
洛谷 P4437 [HNOI/AHOI2018]排列(贪心+堆,思维题)的更多相关文章
- [Bzoj5285][洛谷P4424][HNOI/AHOI2018]寻宝游戏(bitset)
P4424 [HNOI/AHOI2018]寻宝游戏 某大学每年都会有一次Mystery Hunt的活动,玩家需要根据设置的线索解谜,找到宝藏的位置,前一年获胜的队伍可以获得这一年出题的机会. 作为新生 ...
- [HNOI/AHOI2018]排列 贪心
题面 题解: 把题面的限制换成中文: 如果排在第k位的下标 = 排在第j位的值 ,那么k < j 换一个描述方式: 一个值为x的数要排在第x个数后面. 再换一个描述方式: \(fa[i] = a ...
- 洛谷P4438 [HNOI/AHOI2018]道路(dp)
题意 题目链接 Sol 每当出题人想起他出的HNOI 2018 Day2T3,他都会激动的拍打着轮椅 读题比做题用时长系列... \(f[i][a][b]\)表示从根到\(i\)的路径上,有\(a\) ...
- luogu P4437 [HNOI/AHOI2018]排列
luogu 问题本质是把\(a_i\)作为\(i\)的父亲,然后如果有环就不合法,否则每次要取数,要满足取之前他的父亲都被取过(父亲为0可以直接取),求最大价值 贪心想法显然是要把权值大的尽量放在后面 ...
- 洛谷P4425 [HNOI/AHOI2018]转盘(线段树)
题意 题目链接 Sol 首先猜一个结论:对于每次询问,枚举一个起点然后不断等到某个点出现时才走到下一个点一定是最优的. 证明不会,考场上拍了3w组没错应该就是对的吧... 首先把数组倍长一下方便枚举起 ...
- 洛谷P4424 [HNOI/AHOI2018]寻宝游戏(思维题)
题意 题目链接 Sol 神仙题Orz Orz zbq爆搜70.. 考虑"与"和"或"的性质 \(0 \& 0 = 0, 1 \& 0 = 0\) ...
- [洛谷P4436] HNOI/AHOI2018 游戏
问题描述 一次小G和小H在玩寻宝游戏,有n个房间排成一列,编号为1,2,...,n,相邻的房间之间都有一道门.其中一部分门上锁(因此需要有对应的钥匙才能开门),其余的门都能直接打开.现在小G告诉了小H ...
- 洛谷 P4426 - [HNOI/AHOI2018]毒瘤(虚树+dp)
题面传送门 神仙虚树题. 首先考虑最 trival 的情况:\(m=n-1\),也就是一棵树的情况.这个我相信刚学树形 \(dp\) 的都能够秒掉罢(确信).直接设 \(dp_{i,0/1}\) 在表 ...
- 【LG4437】[HNOI/AHOI2018]排列
[LG4437][HNOI/AHOI2018]排列 题面 洛谷 题解 题面里这个毒瘤的东西我们转化一下: 对于\(\forall k,j\),若\(p_k=a_{p_j}\),则\(k<j\). ...
随机推荐
- XSS_Labs靶场通关
XSS-labs靶场(1-20) 开始通关! 0x01 (直接漏洞注入) 反射型xss注入 1.遇到?name=text,尝试参数注入 注入语句: <script>alert('xss ...
- Sequence Model-week2编程题1-词向量的操作【余弦相似度 词类比 除偏词向量】
1. 词向量上的操作(Operations on word vectors) 因为词嵌入的训练是非常耗资源的,所以ML从业者通常 都是 选择加载训练好 的 词嵌入(Embedding)数据集.(不用自 ...
- Java中的函数式编程(七)流Stream的Map-Reduce操作
写在前面 Stream 的 Map-Reduce 操作是Java 函数式编程的精华所在,同时也是最为复杂的部分.但一旦你啃下了这块硬骨头,那你就真正熟悉Java的函数式编程了. 如果你有大数据的编程经 ...
- 修改linux 两种时间的方法
1,整理了一下怎么修改linux 两种时间的方法. 硬件时间:hwclock 或者clock,设置的方法是 hwclock --set --date="05/12/2018 12:30:50 ...
- Docker+nginx搭建tomcat集群
1.环境准备: a.宿主机CentOS7 b.连接工具FinalShell c.镜像nginx1.20.1,tomcat (镜像拉取:docker pull 镜像名称) 2.创建nginx文件夹,to ...
- windows server 2012 开机运行一段时间死机的故障
环境: 物理机:华为2288 V5 虚拟化:esxi 6.5.2 虚拟操作系统 windwos server 2012 标准版 内安装sql server 和其他应用软件 故障描述:window se ...
- openssh 8.2 升级 8.3
openssh 8.2 存在安全漏洞,升级为 openssh 8.3 需要安装的包:openssh-8.3p1.tar.gz.zlib-1.2.11.tar.gz.openssl-1.1.1g.tar ...
- Java学习笔记:GUI基础
一:我们使用到的java GUI的API可以分为3种类: 组件类(component class) 容器类(container class) 辅助类(helper class) 1:组件类:组件类是用 ...
- C++ 函数模板实现原理剖析
C++ 函数模板实现机制原理剖析 重点 编译器并不是把函数模板处理成能够处理任意类的函数 编译器从函数模板通过具体类型来产生不同的函数 编译器会对函数模板进行两次编译 (1)在声明的位置对模板代码进行 ...
- airflow 并发上不去
airflow.cfg parallelism配置是否合适 任务池slot是否足够