提交地址。


关于lyd给的倍增方法,即从当前枚举向后的$2^k$长度($k$从$1$开始),如果可行就将$k$加一以扩大范围,不可行时将范围不断减半直至$0$。

举个例子,假设当下在1,目标答案是13,那么枚举的范围变化情况是$2$,$4$,$8$,$16$(不行,且范围开始缩小),$12$,$14$(不行),$13$,$13$(范围缩小至0)。

并没有看出这样倍增有什么好处。复杂度可证也是$O(logN)$的,但是不是会带个2左右的常数么。。具lyd所说,当目标答案位置较近时会加快效率。

但是这不影响整体复杂度啊。。带着不很理解的态度看了例题才明白这种倍增姿势相较于二分或者从大到小二倍增的优势所在。


题意:数列$N \leqslant 500000$,求划分最少区间使得每段区间任选$M$对数字(不重复选,不够$M$对的时候能选多少选多少)的差的平方之和小于$K$。


首先很容易证明(cai ce)到对于一列数的最大的上述价值就是将最大数减最小数平方加上次大数减次小数平方加上......也就是排序后头尾相配。可以微调法证明任意一种其他策略不会更优。此贪心为关键之一。

然后因为满足要求的一段区间显然越长越好,所以从起点开始拓展,拓展到最远的地方记下来,反复接替拓展,一定是最小区间数。又因为答案随数列增长是有单调性的,所以可以二分检查找到最远的符合要求的右端点。

每次check时候对区间进行排序,加上二分以及区间数的复杂度,最坏$O(N^2log^2N)$。

可以发现,二分时候每次排序都是一个$O(NlogN)$,因为最坏可能答案在比较靠右的位置。这个时候,lyd给出的倍增方案就派上用场了。


lyd书上描述的倍增方法,总是将复杂度限制在$log($答案区间的长度$)$。假若我们采用这种倍增,那么每次枚举的区间最长长度假设为$K$,则找到这样一个区间的复杂度是$O(Klog^2K)$。(倍增一个$logK$,每次排序一个$KlogK$)而不是原来完整的N。

那么,每个区间的复杂度累加起来不会超过$O(Nlog^2N)$。很容易证。所以通过限制枚举次数在答案对数内,累加起来就比原来少一个log。这就是这种倍增优势。


但是$O(Nlog^2N)$仍然过不了。考虑到每次都要排序,前面已经可行的区间又被拉进来排了一次,显然浪费时间。于是可以只对当前试探的这段区间排序后,和原来已排好序的两个数列归并。归并完求代价,判断是否满足要求。

每次只对新的一小段排序,总体累加起来每次拓展的排序复杂度是$O(KlogK)$的(对于枚举过头了的区间,虽然不断缩小不断重复排序,但由于每次的排序复杂度折半,总体不会超过原来大区间的两倍)。

而$logK$次倍增每次复制需要$O(K)$,所以也是$O(KlogK)$.

最后,总体累加,复杂度$O(NlogN)$。

这题给予我们几个启发:二分和倍增都可用于有单调性的查找,有时候两者没有什么区别,但有时的check函数复杂度和答案位置有关倍增可以通过限制此条件使得总体复杂度优化掉。


代码写起来的话还是很少的。

WA记录

  • line61智障没考虑边界。
  • line49笔误。。
  • line59每次初始值。。哎可能是我倍增没有操作好
 #include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define dbg(x) cerr<<#x<<" = "<<x<<endl
#define _dbg(x,y) cerr<<#x<<" = "<<x<<" "<<#y<<" = "<<y<<endl
using namespace std;
typedef long long ll;
template<typename T>inline char MIN(T&A,T B){return A>B?A=B,:;}
template<typename T>inline char MAX(T&A,T B){return A<B?A=B,:;}
template<typename T>inline T _min(T A,T B){return A<B?A:B;}
template<typename T>inline T _max(T A,T B){return A>B?A:B;}
namespace io{
const int SIZE = ( << ) + ;
char ibuf[SIZE], *iS, *iT, obuf[SIZE], *oS = obuf, *oT = oS + SIZE - , c, qu[]; int f, qr;
#define gc() (iS == iT ? (iT = (iS = ibuf) + fread (ibuf, 1, SIZE, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++)
inline void flush (){fwrite (obuf, , oS - obuf, stdout);oS = obuf;}
inline void putc (char x){*oS ++ = x;if (oS == oT) flush ();}
template <class I>
inline void read(I &x) {for (f = , c = gc(); c < '' || c > ''; c = gc()) if (c == '-') f = -;
for (x = ; c <= '' && c >= ''; c = gc()) x = x * + (c & ); x *= f;}
template <class I>
inline void print (I x){
if (!x) putc (''); if (x < ) putc ('-'), x = -x;while(x) qu[++ qr] = x % + '', x /= ;while (qr) putc (qu[qr--]);}
struct Flusher_ {~Flusher_(){flush();}}io_flusher_;
}
using io::read;
using io::putc;
using io::print;
const int N=+;
int a[N],tmp[N],b[N],c[N];//c:临时排序数组 b:已归并好的数组 tmp:临时归并数组
ll k;
int T,n,m,ans; inline void Merge(int L,int R,int r){
int i=L,j=R+;
for(register int k=L;k<=r;++k)
if(b[i]<c[j]&&i<=R||j>r)tmp[k]=b[i++];
else tmp[k]=c[j++];
}
inline ll calc(int L,int R,int r){
if(r==R)return k+;//_dbg(L,R),dbg(r);
for(register int i=R+;i<=r;++i)c[i]=a[i];
sort(c+R+,c+r+);
Merge(L,R,r);
ll ret=;//for(register int i=L;i<=r;++i)printf("%d ",tmp[i]);puts("");
for(register int i=L;i<=L+_min(m,(r-L+>>))-;++i)ret+=(tmp[r-i+L]-tmp[i])*1ll*(tmp[r-i+L]-tmp[i]);
return ret;
} int main(){//freopen("test.in","r",stdin);//freopen("test.out","w",stdout);
read(T);while(T--){
read(n),read(m),read(k);
for(register int i=;i<=n;++i)read(a[i]);
ans=;int L=,R=,p,r;
while(L<=n){
p=;b[L]=a[L];
while(p){
if(calc(L,R,r=_min(n,R+p))<=k){
for(register int i=L;i<=r;++i)b[i]=tmp[i];
R=r,p<<=;
}
else p>>=;
}
L=R+,R=L,++ans;
}
print(ans);putc('\n');
}
return ;
}

hihocoder1384/CH0601 Genius ACM[贪心+倍增+归并排序]的更多相关文章

  1. CH0601 Genius ACM【倍增】【归并排序】

    0601 Genius ACM 0x00「基本算法」例题 描述 给定一个整数 M,对于任意一个整数集合 S,定义“校验值”如下: 从集合 S 中取出 M 对数(即 2∗M 个数,不能重复使用集合中的数 ...

  2. $CH0601\ Genius\ ACM$ 倍增优化DP

    ACWing Description 给定一个长度为N的数列A以及一个整数T.我们要把A分成若干段,使得每一段的'校验值'都不超过N.求最少需要分成几段. Sol 首先是校验值的求法: 要使得'每对数 ...

  3. ACM-ICPC Beijing 2016 Genius ACM(倍增+二分)

    描述 给定一个整数 M,对于任意一个整数集合 S,定义“校验值”如下: 从集合 S 中取出 M 对数(即 2∗M 个数,不能重复使用集合中的数,如果 S 中的整 数不够 M 对,则取到不能取为止),使 ...

  4. AcWing:109. 天才ACM(倍增 + 归并排序)

    给定一个整数 MM,对于任意一个整数集合 SS,定义“校验值”如下: 从集合 SS 中取出 MM 对数(即 2∗M2∗M 个数,不能重复使用集合中的数,如果 SS 中的整数不够 MM 对,则取到不能取 ...

  5. hihocoder--1384 -- Genius ACM (倍增 归并)

    题目链接 1384 -- Genius ACM 给定一个整数 m,对于任意一个整数集合 S,定义“校验值”如下:从集合 S 中取出 m 对数(即 2*M 个数,不能重复使用集合中的数,如果 S 中的整 ...

  6. Contest Hunter 0601 Genius ACM

    Genius ACM Advanced CPU Manufacturer (ACM) is one of the best CPU manufacturer in the world. Every d ...

  7. [hihocoder #1384] Genius ACM 解题报告(倍增)

    题目链接:http://hihocoder.com/problemset/problem/1384 题目大意: 给定一个整数 M,对于任意一个整数集合 S,定义“校验值”如下: 从集合 S 中取出 M ...

  8. hihoCoder#1384 : Genius ACM

    对于一个固定的区间$[l,r]$,显然只要将里面的数字从小到大排序后将最小的$m$个和最大的$m$个配对即可. 如果固定左端点,那么随着右端点的右移,$SPD$值单调不降,所以尽量把右端点往右移,贪心 ...

  9. Codeforces 980E The Number Games 贪心 倍增表

    原文链接https://www.cnblogs.com/zhouzhendong/p/9074226.html 题目传送门 - Codeforces 980E 题意 $\rm Codeforces$ ...

随机推荐

  1. CDC类介绍

    CDC类 中文介绍 CDC类定义的是设备上下文对象的类. CDC对象提供处理显示器或打印机等设备上下文的成员函数,以及处理与窗口客户区对应的显示上下文的成员. 通过CDC对象的成员函数进行所有的绘图. ...

  2. gitlab 数据目录迁移

    一般情况下,采用gitlab作为版本管理工具,内网环境需要搭建gitlab服务器,安装好gitlab应用之后,就开始使用,但是随着时间的推移,发现gitlab的repository会越来越大.一般,从 ...

  3. java安全停止线程

    Thread.stop()是一个被废弃的方法,不被推荐使用的原因是stop方法太过于暴力,强行把执行到一半的线程终止,并且会立即释放这个线程所有的锁.会破坏了线程中引用对象的一致性. 使用判断标志位的 ...

  4. 【转载】深度学习中softmax交叉熵损失函数的理解

    深度学习中softmax交叉熵损失函数的理解 2018-08-11 23:49:43 lilong117194 阅读数 5198更多 分类专栏: Deep learning   版权声明:本文为博主原 ...

  5. 【机器学习】深入理解拉格朗日乘子法(Lagrange Multiplier) 和KKT条件

    在求取有约束条件的优化问题时,拉格朗日乘子法(Lagrange Multiplier) 和KKT条件是非常重要的两个求取方法,对于等式约束的优化问题,可以应用拉格朗日乘子法去求取最优值:如果含有不等式 ...

  6. 强化学习复习笔记 - DEEP

    Outline 激活函数 使用逼近器的特点: 较少数量的参数表达复杂的函数 (计算复杂度) 对一个权重的调整可以影响到很多的点 (泛化能力) 多种特征表示和逼近器结构 (多样性) 激活函数 Sigmo ...

  7. Swool的安装与使用

    1.swoole的安装 //php最好用7.2以上的.直接去网站下载下来,然后与php一样编译安装. git下来后,因为没有config文件,故先在swool下载目录下执行: /.../php/bin ...

  8. c++ 读入优化通用模板

    struct ioss { #define endl '\n' ; char obuf[LEN], *oh = obuf; std::streambuf *fb; ioss() { ios::sync ...

  9. 面向对象编程 OOP

    OOP,Object Oriented Programming,原来就是面向对象的编程. 面向对象是一种对现实世界理解和抽象的方法,是计算机编程技术发展到一定阶段后的产物. OOD,Object Or ...

  10. 阿里云服务器挖矿脚本bioset攻击解决

    1.问题出现 一大早刚起床,阿里云就给我发了一条短信,提醒我服务器出现紧急安全事件:挖矿程序 阿里云“贴心”地提供了解决方法,不过需要购买企业版的安全服务,本着能自己动手就不花钱原则自己搞了起来 于是 ...