CF1039E Summer Oenothera Exhibition 贪心、根号分治、倍增、ST表
感谢这一篇博客的指导(Orzwxh)
$PS$:默认数组下标为$1$到$N$
首先很明显的贪心:每一次都选择尽可能长的区间
不妨设$d_i$表示在取当前$K$的情况下,左端点为$i$的所有满足条件的区间中最大的右端点$+1$,然后连边$(i,d_i)$
那么我们就需要求一条链的长度,并支持动态修改某一些边
是不是有些印象?与弹飞绵羊极为相似,没有做过的可以先去感受一下……
上面那道题有两种做法:$LCT$与分块,所以这一道题就衍生出了$O(n\sqrt{n}logn)$的基于$LCT$的做法(安利chd的题解qaq)和$O(n^\frac{5}{3} + n^\frac{4}{3}logn)$的基于分块的做法。下面要谈的是后者。
首先因为输入中$K$的变化是无序的,所以修改会十分麻烦。不妨将询问离线,按照$W-K$从小到大排序,那么$d_i$的改变就是不降的。
设块长为$a$,又设$pre_i$,它需满足$pre_i - i \leq a$、$d_{pre_i} - i > a$、$pre_i$与$i$在同一条链上(如果对于链上的所有点都不满足条件,则令$pre_i=N+1$表示可以直接从$i$点经过不超过一个块长跳出去),维护$cnt_i$表示$i$与$pre_i$之间的点的个数(不计$pre_i$)。
对于每一次查询操作,我们就从$1$开始跳$pre$,每一次都加上对应的$cnt$,那么询问的答案就是$\sum cnt - 1$($1$不需要被算进去)。
但是会发现如果$d_i - i > a$,$pre_i=i$就会死循环,所以我们对与$pre_i=i$的情况使用倍增找到它下一次要跳到哪一个点。
可以知道查询的复杂度是$O(\frac{n}{a}qlogn)$
对于修改,每一个点$i$最多都会被修改$a$次,每一次对于点$i$的修改我们只需要维护$i-a$到$i-1$的$pre$和$cnt$,修改的复杂度就是$O(na^2)$
可知在$a = n^\frac{1}{3}$时该算法取到最小复杂度$O(n^\frac{5}{3} + n^\frac{5}{3}logn)$
喂这个算法和暴力有什么区别啊
可以发现我们的复杂度瓶颈在查询的倍增上,考虑如何优化倍增的复杂度。
因为当$d_i - i > n^\frac{1}{3}$时,前面的所有点的$pre$都是不会因为$d_i$的改变而改变的,所以当$d_i - i > n^\frac{1}{3}$时,单次修改的复杂度变为了O(1)
所以我们可以考虑:当$n^\frac{1}{3} < d_i - i \leq b$时暴力移动$d_i$的值进行更新,当$d_i - i > b$时进行倍增
那么查询的复杂度就变为$O(n^\frac{5}{3} + \frac{n}{b}qlogn)$,修改的复杂度变为了$O(n^\frac{5}{3} + nb)$当$b=n^\frac{2}{3}$时总复杂度取到最小值$O(n^\frac{5}{3} + n^\frac{4}{3}logn)$
然后这道题就做完了
所以这道题的思路就是一个奇怪的与根号分治相似的东西,有点三合一的意思:
①$d_i-i \leq n^\frac{1}{3}$,通过维护$pre_i$与$cnt_i$压缩路径
②$n^\frac{1}{3} < d_i-i \leq n^\frac{2}{3}$,暴力移动$d_i$位置进行更新
③$n^\frac{2}{3} < d_i-i$,倍增找到下一次更新的点
一些实现上的细节:
①在$d_i - i \leq n ^ \frac{1}{3}$部分判断哪一些点需要修改可以使用堆进行维护(再在这里Orzwxh),维护$[i,d_i]$的极差然后扔到堆里面,每一次小于等于当前$W-K$的堆中的点就是需要重新更新$pre$与$cnt$的点
②倍增使用$ST$表进行实现
③维护$n^\frac{1}{3} < d_i - i \leq n^\frac{2}{3}$的部分在计算答案时进行更新即可,不需要额外维护
④根据③,在$d_i - i \leq n ^ \frac{1}{3}$时,不能直接把$pre_i$的贡献算上然后跳到$nxt_{pre_i}$,因为$nxt_{pre_i}$可能还没有更新导致跳到一些奇怪的地方
#include<bits/stdc++.h>
#define PII pair < int , int >
#define st first
#define nd second
//This code is written by Itst
using namespace std;
inline int read(){
;
char c = getchar();
;
while(!isdigit(c))
c = getchar();
while(isdigit(c)){
a = (a << ) + (a << ) + (c ^ ');
c = getchar();
}
return f ? -a : a;
}
inline int max(int a , int b){
return a > b ? a : b;
}
inline int min(int a , int b){
return a < b ? a : b;
}
;
int N , W , Q , logN , j_small , j_big;
][MAXN][] , pre[MAXN] , nxt[MAXN] , nMax[MAXN] , nMin[MAXN] , cnt[MAXN] , len[MAXN] , ans[MAXN];
bool vis[MAXN];
priority_queue < PII > q , mod;
deque < int > v;
void input(){
N = read();
j_small = pow(N , );
j_big = pow(N , );
W = read();
Q = read();
; i <= N ; ++i)
num[i] = ST[][i][] = ST[][i][] = read();
; i <= Q ; ++i)
q.push(PII(read() , i));
}
void init(){
; << i <= N ; ++i)
; j + ( << i) - <= N ; ++j){
ST[i][j][] = min(ST[i - ][j][] , ST[i - ][j + ( << (i - ))][]);
ST[i][j][] = max(ST[i - ][j][] , ST[i - ][j + ( << (i - ))][]);
}
logg2[] = -;
; i <= N ; ++i){
logg2[i] = logg2[i >> ] + ;
nxt[i] = i + ;
pre[i] = min(N + , j_small + i);
cnt[i] = pre[i] - i;
nMax[i] = max(num[i] , num[i + ]);
nMin[i] = min(num[i] , num[i + ]);
if(i != N)
mod.push(PII(nMin[i] - nMax[i] , i));
}
logN = ;
<= N)
logN <<= ;
logN = logg2[logN];
}
void upd(int p , PII t){
mod.pop();
while(nxt[p] <= N && nMax[p] - nMin[p] <= t.st && nxt[p] - p <= j_big){
++nxt[p];
if(nxt[p] <= N){
if(nMax[p] < num[nxt[p]])
nMax[p] = num[nxt[p]];
if(nMin[p] > num[nxt[p]])
nMin[p] = num[nxt[p]];
}
}
if(nxt[p] <= N && nxt[p] - p <= j_small)
mod.push(PII(nMin[p] - nMax[p] , p));
pre[p] = p;
cnt[p] = len[p] = ;
v.clear();
v.push_back(p);
vis[p] = ;
while(pre[p] <= N && nxt[pre[p]] - p <= j_small){
pre[p] = nxt[pre[p]];
++cnt[p];
v.push_back(pre[p]);
}
; i && i >= p - j_small ; --i)
if(vis[nxt[i]]){
while(v.back() - i > j_small)
v.pop_back();
pre[i] = v.back();
cnt[i] = len[nxt[i]] + v.size();
len[i] = len[nxt[i]] + ;
vis[i] = ;
}
memset(vis + max(p - j_small , ) , , ));
}
void work(){
while(!q.empty()){
PII t = q.top();
q.pop();
t.st = W - t.st;
while(!mod.empty() && -mod.top().st <= t.st)
upd(mod.top().nd , t);
, all = ;
while(p <= N){
int cur = nxt[p] - p;
if(cur <= j_small){
all += cnt[p];
p = pre[p];
}
else{
++all;
while(nxt[p] <= N && nMax[p] - nMin[p] <= t.st && cur <= j_big){
++nxt[p];
++cur;
if(nxt[p] <= N){
if(nMax[p] < num[nxt[p]])
nMax[p] = num[nxt[p]];
if(nMin[p] > num[nxt[p]])
nMin[p] = num[nxt[p]];
}
}
if(cur <= j_big)
p = nxt[p];
else{
, cMin = 1e9;
; --j)
<< j) - <= N){
]) , n = min(cMin , ST[j][p][]);
if(m - n <= t.st){
cMax = m;
cMin = n;
p += << j;
}
}
}
}
}
ans[t.nd] = all;
}
}
void output(){
; i <= Q ; ++i)
printf();
}
int main(){
#ifndef ONLINE_JUDGE
freopen("in" , "r" , stdin);
freopen("out" , "w" , stdout);
#endif
input();
init();
work();
output();
;
}
CF1039E Summer Oenothera Exhibition 贪心、根号分治、倍增、ST表的更多相关文章
- CF1039E Summer Oenothera Exhibition 根号分治,LCT,ST表
CF1039E Summer Oenothera Exhibition LG传送门 根号分治好题. 可以先看我的根号分治总结. 题意就是给出长度为\(n\)的区间和\(q\)组询问以及一个\(w\), ...
- [CF1039D]You Are Given a Tree[贪心+根号分治]
题意 给你\(n\)个点的树,其中一个简单路径的集合被称为\(k\)合法当且仅当树的每个节点最多属于一条路径,且每条路径包含\(k\)个节点.对于每个\(k(k \in [1,n])\),输出最多的\ ...
- NOI.AC#2266-Bacteria【根号分治,倍增】
正题 题目链接:http://noi.ac/problem/2266 题目大意 给出\(n\)个点的一棵树,有一些边上有中转站(边长度为\(2\),中间有一个中转站),否则就是边长为\(1\). \( ...
- 【BZOJ3784】树上的路径 点分治序+ST表
[BZOJ3784]树上的路径 Description 给定一个N个结点的树,结点用正整数1..N编号.每条边有一个正整数权值.用d(a,b)表示从结点a到结点b路边上经过边的权值.其中要求a< ...
- 浅谈 倍增/ST表
命题描述 给定一个长度为 \(n\) 的序列,\(m\) 次询问区间最大值 分析 上面的问题肯定可以暴力对吧. 但暴力肯定不是最优对吧,所以我们直接就不考虑了... 于是引入:倍增 首先,倍增是个什么 ...
- P7599-[APIO2021]雨林跳跃【二分,倍增,ST表】
正题 题目链接:https://www.luogu.com.cn/problem/P7599 题目大意 \(n\)棵树,在某棵树上时可以选择向左右两边第一棵比它高的树跳,现在\(q\)次询问从\([A ...
- [CF1039E]Summer Oenothera Exhibition[根号分治+lct]
题意 给一个长度为 \(n\) 的序列, \(q\) 次询问,次给一个 \(k_i\) ,问最少将序列划分成多少次,满足每一段的极差不超过\(w−k_i\). \(1 \leq n, q \leq 1 ...
- poj 3264 倍增 ST表
#include<iostream> #include<cmath> using namespace std; ; int a[maxn]; ]; ]; int quick(i ...
- 【BZOJ1047】[HAOI2007]理想的正方形 (倍增ST表)
[HAOI2007]理想的正方形 题目描述 有一个\(a*b\)的整数组成的矩阵,现请你从中找出一个\(n*n\)的正方形区域,使得该区域所有数中的最大值和最小值的差最小. 输入输出格式 输入格式: ...
随机推荐
- CSS水平居中的三种方法
CSS中经常会用到元素居中,那么今天我为大家分享几种水平居中的方法,下面代码都可以达到同样的居中效果,来不及解释了,快上马(码): 一.margin : 0 auto; <head> &l ...
- css3 Box model 与 Box-sizing
1.Box Model(盒模型) CSS中的Box Model分为两种:第一种是W3C的标准模型,另一种是IE的传统模型.它们的相同之处是对元素的width.height.padding.border ...
- 听说你还不会用Dagger2?Dagger2 For Android最佳实践教程
前言 Dagger2是现在非常火的一个依赖注入框架,目前由Google维护,在Github上面已经有12K star了.Dagger2的入门门槛其实是比较高的,据了解,目前有很多Android工程师对 ...
- 安卓APP应用在各大应用市场上架方法整理
想要把APP上架到应用市场都要先注册开发者账号才可以.这里的方法包括注册帐号和后期上架及一些需要注意的问题.注意:首次提交应用绝对不能随便删除,否则后面再提交会显示应用APP冲突,会要求走应用认领流程 ...
- 给电脑插上无线网卡,变成路由器----Windows系统承载网络的使用
1. 以管理员身份运行命令提示符(PowerShell) 2. 启用并设定虚拟wifi网卡 netsh wlan set hostednetwork mode=allow ssid=wifi名称 ke ...
- Linux : Vim 使用与配置 (附 GitHub 自动化配置脚本)
由于经常使用 vim 编辑配置文件,有时候也会进行使用vim 编写一些脚本和c/c++ 程序,所以配置一个常用的 vim 是很是必要的.这篇博文主要是记录vim使用和配置相关的一些知识点. 关于vim ...
- 【PAT】B1054 求平均值(20 分)
一开始就想用sscanf和sprintf去实现,但是没有设计好思路,卡住了. 看了网上别人的博客,都是用的很复杂的逻辑分别判断,一言不合就七八十航代码 今天早上看了柳神的代码,发现跟自己的思路一样,开 ...
- percona-toolkit大表操作DDL使用
1. 系统与安装数据库 [root@zhang ~]# cat /etc/redhat-release # 也可以使用其他版本 CentOS Linux release (Core) [root@zh ...
- 关于UIPageViewController那些事
一.前言 这些天有新生问及UIPageViewController这个视图控制器,自己原来没有用过,所以就看了一下相关的知识,就写了下来,分享一下经验. 主要的关于这个控制器的内容就从例子中去解说了. ...
- Alpha冲刺! Day9 - 砍柴
Alpha冲刺! Day9 - 砍柴 今日已完成 晨瑶:继续补充gitkraken教程. 昭锡:实现主页基本布局. 永盛:进一步了解了框架,为框架生成的模型填充了假数据到数据库. 立强:文章模块基本实 ...