首先,容易得到判断一个子串为“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——线段树的更多相关文章

  1. 2016暑假多校联合---Rikka with Sequence (线段树)

    2016暑假多校联合---Rikka with Sequence (线段树) Problem Description As we know, Rikka is poor at math. Yuta i ...

  2. Wow! Such Sequence!(线段树4893)

    Wow! Such Sequence! Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others ...

  3. HDU 6047 Maximum Sequence(线段树)

    题目网址:http://acm.hdu.edu.cn/showproblem.php?pid=6047 题目: Maximum Sequence Time Limit: 4000/2000 MS (J ...

  4. 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 ...

  5. ZOJ 4100 浙江省第16届大学生程序设计竞赛 A题 Vertices in the Pocket 线段树+并查集

    正赛的时候完全没看这个题,事后winterzz告诉我他想出来的解法. 首先题意是给出n个点,m次操作. 操作有一种是连接两个点,另一种是求此时再为这个图连k条边,最少和最多能有几个联通块. 最少的求法 ...

  6. Codeforces 486E LIS of Sequence(线段树+LIS)

    题目链接:Codeforces 486E LIS of Sequence 题目大意:给定一个数组.如今要确定每一个位置上的数属于哪一种类型. 解题思路:先求出每一个位置选的情况下的最长LIS,由于開始 ...

  7. K - Japan(线段树)

    Japan plans to welcome the ACM ICPC World Finals and a lot of roads must be built for the venue. Jap ...

  8. 【BZOJ】3038: 上帝造题的七分钟2(线段树+暴力)

    http://www.lydsy.com:808/JudgeOnline/problem.php?id=3038 这题我就有得吐槽了,先是线段树更新写错,然后不知哪没pushup导致te,精度问题sq ...

  9. 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 ...

  10. codevs2492上帝造题的七分钟 2(线段树)

    /* 区间修改 区间查询 可以用线段树搞 但是一般的标记下放对这个题好像不合适 只能改叶子 然后更新父亲(虽然跑的有点慢) 小优化:如果某个点是1 就不用再开方了 所以搞一个f[i]标记 i 这个点还 ...

随机推荐

  1. uva 1632 Alibaba

    题意: 一个人要从如果干个地方拿货,每个地方的货物是有存在时间的,到了某个时间之后就会消失. 按照位置从左到右给出货物的位置以及生存时间,这个人选择一个最优的位置出发,问拿完货物的最少时间. 思路: ...

  2. mysql 创建用户,删除用户,增加权限

    1,查询mysql 数据库已经存在的用户: SELECT USER,HOST FROM MYSQL.USER; 2,创建mysql 用户: '; USERNAME:用户名 HOST:主机,PASSWO ...

  3. 如何用新安装的jdk替换掉Linux系统默认jdk

    版主写的非常棒: http://blog.csdn.net/u011364306/article/details/48375653

  4. sqoop从hive导入数据到mysql时出现主键冲突

    今天在将一个hive数仓表导出到mysql数据库时出现进度条一直维持在95%一段时间后提示失败的情况,搞了好久才解决.使用的环境是HUE中的Oozie的workflow任何调用sqoop命令,该死的o ...

  5. Linux基础命令---文本编辑tee

    tee 将标准输入的内容复制到指定的文件中,同时在标准输出中显示. 此命令的适用范围:RedHat.RHEL.Ubuntu.CentOS.SUSE.openSUSE.Fedora.   1.语法   ...

  6. usdt转入转出出入金开发

    usdt转入转出出入金开发 比特币协议 -> Omni 层协议 -> USDTUSDT是基于比特币omni协议的一种代币: https://omniexplorer.info/asset/ ...

  7. 两眼论&矩阵变现理论结合打造赚钱大模式

    两眼论&矩阵变现理论结合打造赚钱大模式 围棋有一个基本规则,就是一块棋有两只真眼,就是活棋. 围棋没有复杂的规则,它最有趣的地方是没有太多的规则和限制,由此演变出了大千世界,所以古人云“棋如人 ...

  8. 阻塞队列---ArrayBlockingQueue,LinkedBlockingQueue,DelayQueue源码分析

    阻塞队列和非阻塞队列阻塞队列和非阻塞队列的区别:阻塞队列可以自己阻塞,非阻塞队列不能自己阻塞,只能使用队列wait(),notify()进行队列消息传送.而阻塞队列当队列里面没有值时,会阻塞直到有值输 ...

  9. JustOj 1386: 众数的数量

    题目链接:http://oj.jxust.edu.cn/problem.php?id=1386 题目描述 qwn和Q伟N最近沉迷于Battle of Balls,天天翘课玩游戏.因为Q伟N太坑了,所以 ...

  10. python 网页cookie的使用

    网页cookie的使用 Cookie,指某些网站为了辨别用户身份.进行session跟踪而储存在用户本地终端上的数据(通常经过加密) # opener的概念当你获取一个URL你使用一个opener(一 ...