【做题】CF239E. k-d-sequence——线段树
首先,容易得到判断一个子串为“good k-d sequence”的方法:
- 子串中没有重复元素,且所有元素模d相等。
- 记mx为除以d的最大值,mn为除以d的最小值,则\(mx-mn<=r-l+k\)。
然后,我们对于每一段极大的元素同模的子串,处理\(d=1\)的情况。
显然,我们需要枚举一个端点。这里,我们从大到小枚举左端点。(当然,从小到大枚举右端点也是可行的)
我们使用单调栈和线段树,可以维护每个位置\(mx-mn\)的值。然后,因为对于每一个位置,\(r\)是固定的,所以我们把\(r\)移到左边。即有不等式\(mx-mn-r<=k-l\)。
然后,我们需要确定最右边的\(mx-mn-r<=k-l\)的元素位置,这个线段树上二分就可以了。
最后还有两个细节:
- 为避免出现重复元素,线段树上二分时有限制。
- 特判\(d=0\)的情况。
时间复杂度\(O(nlogn)\)。
#include <bits/stdc++.h>
using namespace std;
const int BAS = 1e9, N = 200010;
struct node {
int mn,tag;
inline void operator += (int x) {
mn += x;
tag += x;
}
inline void reset() {
mn = tag = 0;
}
} t[N << 2];
void push_down(int x) {
t[x<<1] += t[x].tag;
t[x<<1|1] += t[x].tag;
t[x].tag = 0;
}
void push_up(int x) {
if (t[x].tag) push_down(x);
t[x].mn = min(t[x<<1].mn,t[x<<1|1].mn);
}
void modify(int x,int l,int r,int v,int lp,int rp) {
if (lp > r || rp < l) return;
if (lp >= l && rp <= r)
return (void)(t[x] += v);
int mid = (lp + rp) >> 1;
modify(x<<1,l,r,v,lp,mid);
modify(x<<1|1,l,r,v,mid+1,rp);
push_up(x);
}
int dfs(int x,int lim,int v,int lp,int rp) {
if (t[x].mn > v) return -1;
if (lp == rp) return lp;
push_down(x);
int mid = (lp + rp) >> 1;
if (t[x<<1|1].mn <= v && mid + 1 <= lim) {
int res = dfs(x<<1|1,lim,v,mid+1,rp);
if (~res) return res;
}
return dfs(x<<1,lim,v,lp,mid);
}
int n,k,d,arr[N],len;
map<int,int> mp;
int tmp[N];
struct data_sta {
int l,r,val;
inline bool operator < (const data_sta& x) const {
return val < x.val;
}
} st[2][N];
int top[2];
struct data_ans {
int l,r;
inline bool operator < (const data_ans& x) const {
return r - l + 1 != x.r - x.l + 1 ? \
r - l + 1 > x.r - x.l + 1 : l < x.l;
}
};
data_ans solve() {
mp.clear();
data_sta tp;
data_ans res = (data_ans) {len,-1};
int cur = len, rec;
top[0] = top[1] = 0;
for (int i = len ; i >= 1 ; -- i) {
if (mp[tmp[i]]) cur = min(cur,mp[tmp[i]] - 1);
mp[tmp[i]] = i;
tp = (data_sta) {i,i,tmp[i]};
while (top[0] && st[0][top[0]].val < tp.val) {
modify(1,st[0][top[0]].l,st[0][top[0]].r,-st[0][top[0]].val,1,len);
tp.r = st[0][top[0]--].r;
}
st[0][++top[0]] = tp;
modify(1,tp.l,tp.r,tp.val,1,len);
tp = (data_sta) {i,i,tmp[i]};
while (top[1] && st[1][top[1]].val > tp.val) {
modify(1,st[1][top[1]].l,st[1][top[1]].r,st[1][top[1]].val,1,len);
tp.r = st[1][top[1]--].r;
}
st[1][++top[1]] = tp;
modify(1,tp.l,tp.r,-tp.val,1,len);
modify(1,i,i,-i,1,len);
rec = dfs(1,cur,k - i,1,len);
if (~rec) res = min(res,(data_ans) {i,rec});
}
for (int i = 1 ; i <= (len << 2) ; ++ i)
t[i].reset();
return res;
}
int special_solve() {
int res = 0, p = -1;
for (int i = 1, j; i <= n ; i += j) {
j = 1;
while (arr[i+j] == arr[i] && i + j <= n) ++ j;
if (res < j) res = j, p = i;
}
printf("%d %d\n",p,p + res - 1);
return 0;
}
int main() {
scanf("%d%d%d",&n,&k,&d);
for (int i = 1 ; i <= n ; ++ i)
scanf("%d",&arr[i]), arr[i] += BAS ;
if (d == 0) return special_solve();
data_ans res = (data_ans) {1,1}, tp;
for (int i = 1, j ; i <= n ; i += j) {
j = 1;
while (arr[i+j] % d == arr[i] % d && i + j <= n)
++ j;
len = j;
for (int s = 0 ; s < j ; ++ s)
tmp[s+1] = arr[i+s] / d;
tp = solve();
tp.l += i-1, tp.r += i-1;
res = min(res,tp);
}
printf("%d %d\n",res.l,res.r);
return 0;
}
小结:这样一类题目大概就是要怼着式子简化问题。
【做题】CF239E. k-d-sequence——线段树的更多相关文章
- 2016暑假多校联合---Rikka with Sequence (线段树)
2016暑假多校联合---Rikka with Sequence (线段树) Problem Description As we know, Rikka is poor at math. Yuta i ...
- Wow! Such Sequence!(线段树4893)
Wow! Such Sequence! Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others ...
- HDU 6047 Maximum Sequence(线段树)
题目网址:http://acm.hdu.edu.cn/showproblem.php?pid=6047 题目: Maximum Sequence Time Limit: 4000/2000 MS (J ...
- Codeforces 438D The Child and Sequence - 线段树
At the children's day, the child came to Picks's house, and messed his house up. Picks was angry at ...
- ZOJ 4100 浙江省第16届大学生程序设计竞赛 A题 Vertices in the Pocket 线段树+并查集
正赛的时候完全没看这个题,事后winterzz告诉我他想出来的解法. 首先题意是给出n个点,m次操作. 操作有一种是连接两个点,另一种是求此时再为这个图连k条边,最少和最多能有几个联通块. 最少的求法 ...
- Codeforces 486E LIS of Sequence(线段树+LIS)
题目链接:Codeforces 486E LIS of Sequence 题目大意:给定一个数组.如今要确定每一个位置上的数属于哪一种类型. 解题思路:先求出每一个位置选的情况下的最长LIS,由于開始 ...
- K - Japan(线段树)
Japan plans to welcome the ACM ICPC World Finals and a lot of roads must be built for the venue. Jap ...
- 【BZOJ】3038: 上帝造题的七分钟2(线段树+暴力)
http://www.lydsy.com:808/JudgeOnline/problem.php?id=3038 这题我就有得吐槽了,先是线段树更新写错,然后不知哪没pushup导致te,精度问题sq ...
- Codeforces Round #250 (Div. 1) D. The Child and Sequence 线段树 区间取摸
D. The Child and Sequence Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest ...
- codevs2492上帝造题的七分钟 2(线段树)
/* 区间修改 区间查询 可以用线段树搞 但是一般的标记下放对这个题好像不合适 只能改叶子 然后更新父亲(虽然跑的有点慢) 小优化:如果某个点是1 就不用再开方了 所以搞一个f[i]标记 i 这个点还 ...
随机推荐
- Unity shader学习之阴影
Unity阴影采用的是 shadow map 的技术,即把摄像机放到光源位置上,看不到的地方就有阴影. 前向渲染中,若一光源开启了阴影,Unity会计算它的阴影映射纹理(shadow map),它其实 ...
- ASP.NET JSON(转http://www.360doc.com/content/14/0615/21/18155648_386887590.shtml)
概念介绍还是先简单说说Json的一些例子吧.注意,以下概念是我自己定义的,可以参考.net里面的TYPE的模型设计如果有争议,欢迎提出来探讨!1.最简单:{"total":0} t ...
- Codeforces Round #319 (Div. 2) D
E A tree of size n is an undirected connected graph consisting of n vertices without cycles. Conside ...
- Sitecore xDB基础知识 - 识别用户,联系人,访客,客户
体验数据库(xDB)是Sitecore平台的关键元素,特别是当您希望将解决方案提升到简单的内容管理要求之外时.它用于跟踪您的用户(即联系人,访客,客户)与您网站的互动方式.营销人员可以使用此数据来了解 ...
- sitecore系统教程之Item快速了解
项目是Sitecore网站的基本构建块.项目可以表示构成网页的任何类型的信息,例如,一段文本,媒体文件,布局等. 项目始终具有名称,唯一标识项目的ID,并且它基于定义项目包含的字段的模板.此外,项目可 ...
- Nginx技术研究系列2-基于Redis实现动态路由
上篇博文我们写了个引子: Ngnix技术研究系列1-通过应用场景看Nginx的反向代理 发现了新大陆,OpenResty OpenResty 是一个基于 Nginx 与 Lua 的高性能 Web 平台 ...
- 20155228 2016-2017-2 《Java程序设计》第1周学习总结
20155228 2016-2017-2 <Java程序设计>第1周学习总结 教材学习内容总结 这部分内容是以教材为基础,根据个人的理解来描述的,有的地方的理解和表述可能不规范甚至不正确, ...
- 使用Groovy+Spock轻松写出更简洁的单测
当无法避免做一件事时,那就让它变得更简单. 概述 单测是规范的软件开发流程中的必不可少的环节之一.再伟大的程序员也难以避免自己不犯错,不写出有BUG的程序.单测就是用来检测BUG的.Java阵营中,J ...
- ExtJS5入门
https://www.cnblogs.com/xiaoliu66007/p/7988060.html
- Properties (25)
1.Properties 没有泛型.也是哈希表集合,无序集合.{a=1,b=2,c=3} 2. 读取文件中的数据,并保存到集合 (Properties方法:stringPropertyName ...