nekameleoni

区间查询和修改

给定N,K,M(N个整数序列,范围1~K,M次查询或修改)

如果是修改,则输入三个数,第一个数为1代表修改,第二个数为将N个数中第i个数做修改,第三个数为修改成这个数(例如1 3 5就是修改数组中第3个数,使之变为5)

如果是查询,则输入一个数2,查询N个数中包含1~K每一个数的最短连续子序列的长度

输入

第一行包含整数N、K和M(1 ≤ N,M ≤ 100000,1 ≤ K ≤ 50)

第二行输入N个数,用空格隔开,组成该数组

然后M行表示查询或修改

若是1 p v(1 ≤ p ≤ N,1 ≤ v ≤ K)

若是2则是询问1~K的最短连续子序列长度。

输出

输出必须包含查询的答案,如果不存在则输出-1。

分数分布

对于30%的数据:1≤M,N≤5000。

样例输入1

4 3 5

2 3 1 2

2

1 3 3

2

1 1 1

2


样例输出1

3

-1

4

样例输入2

6 3 6

1 2 3 2 1 1

2

1 2 1

2

1 4 1

1 6 2

2


样例输出2

3

3

4

解释一下题意,如果询问为2则输出包含1-k的最长连续序列,否则进行单点修改。

单点修改,区间查询,明显可以使用线段树,但如何进行区间的维护却是此题的难点。


考虑使用3个量来维护线段树的每一个节点:

1.前缀中,用\(x_{i}\)表示含\(x_{i}\)种不同数字的连续的序列。存储每个\(x_{i}\)的状态(也就是哪\(x_{i}\)个不同的数字,这里可以使用二进制压50位来实现)。

对于多个前缀,如果它们包含的不同的数字相同,我们只需要存储长度最小的那一个,这样可以保证结果最优。

同理后缀也可以这么实现。

2.ans,表示在当前节点表示的区间中,包含\(k\)个不同的数的最短区间的长度,也就是最终要输出的答案。这样的好处是我们可以直接输出根节点的\(ans\)作为答案。


有了前缀和后缀,我们就可以进行合并了。对于节点\(t\)的左儿子\(l\)和右儿子\(r\),当我们要将\(l\)和\(r\)合并成\(t\)时,只需要将\(l\)的后缀和\(r\)的前缀进行合并,得到满足条件的最小值,将它与\(l\)的ans,和\(r\)的ans进行比较,最优的值便可以最为\(t\)的ans。


前缀和后缀的维护比较复杂,这些细节的问题我会在代码中的注释解释(比如二进制的使用,还有前缀和后缀的更新操作都很复杂。)

这里探讨的主要是前缀和后缀的合并操作。

设\(l\)的后缀有\(k_{1}\)个,\(r\)的后缀有\(k_{2}\)个,我们可以考虑暴力枚举每一个\(k_{1}\)和\(k_{2}\)来合并,但这样做时间复杂度过高,所以便可以使用尺取法。

我们可以发现\(l\)若的第\(i\)个后缀与\(r\)的第\(j\)个后缀合并后满足有\(k\)个不同的数字,那么便无需考虑\(j\)之后的后缀。

这便是尺取法的精髓思想。如果你还不懂尺取法,可以去网上找博客看看(或许我以后会写相关的博客?)。


尺取法的时间复杂度是线性的,所以我们整个程序的时间复杂度是\(O(n_{logn}k)\)


代码

#include <iostream>
#include <cstdio>
using namespace std; #define N 100010
#define MAXN 262144
#define T 131071
#define LL long long
#define inf 0x7f7f7f7f
typedef pair<LL,int> P; struct Fake {
P suf[52],pre[52];
int ans,sum;
Fake():ans(inf) {};
}Fuck[MAXN]; int n,k,m; void Merge(int t,int l, int r) {
int ans=inf;
Fuck[t].ans=min(Fuck[l].ans,Fuck[r].ans);//维护ans
Fuck[t].sum=0;
int lenpre=0,lensuf=0;
for(int i=0;i<Fuck[l].sum;i++)
Fuck[t].pre[lenpre++]=Fuck[l].pre[i];//将左儿子的前缀继承到当前节点的前缀
for(int i=0;i<Fuck[r].sum;i++) {
if(lenpre==0 ||( Fuck[r].pre[i].first & Fuck[t].pre[lenpre-1].first) != Fuck[r].pre[i].first) {//如果右节点的前缀包含的不同数数量比左儿子的前缀数量还多,则可以
Fuck[t].pre[lenpre]=Fuck[r].pre[i];//添加进来
if(lenpre>0) Fuck[t].pre[lenpre].first|=Fuck[t].pre[lenpre-1].first;
lenpre++;
}
}
for(int i=0;i<Fuck[r].sum;i++)//后缀处理同理
Fuck[t].suf[lensuf++]=Fuck[r].suf[i];
for(int i=0;i<Fuck[l].sum;i++) {
if(lensuf==0 ||( Fuck[l].suf[i].first & Fuck[t].suf[lensuf-1].first) != Fuck[l].suf[i].first) {
Fuck[t].suf[lensuf]=Fuck[l].suf[i];
if(lensuf>0) Fuck[t].suf[lensuf].first|=Fuck[t].suf[lensuf-1].first;
lensuf++;
}
}
Fuck[t].sum=lenpre;
int j=0;
for(int i=Fuck[l].sum-1;i>=0;i--) {//尺取法合并操作
while(j<Fuck[r].sum) {
if((Fuck[r].pre[j].first | Fuck[l].suf[i].first) == ((1ll<<k)-1))
break;
j++;
}
if(j<Fuck[r].sum) Fuck[t].ans=min(Fuck[t].ans,Fuck[r].pre[j].second - Fuck[l].suf[i].second+1);
}
} void update(int pos,int x) {
int now=T+pos;
Fuck[now].suf[0]= make_pair(1ll<<(x-1),pos) ;//用一个pair类型来表示一个前缀
Fuck[now].pre[0]= make_pair(1ll<<(x-1),pos) ;//first为一个二进制数,表示包含了哪些
Fuck[now].ans=inf;//不同的数字,pos为对应的下标,用于计算答案
Fuck[now].sum=1;//sum统计相应的前后缀数量
while(now/2) {//循环型线段树,从儿子往根节点操作
now/=2;
Merge(now,now<<1,now<<1|1);
}
} int main() {
cin>>n>>k>>m;
for(int i=1,A;i<=n;i++) {
cin>>A;
update(i,A);
}
for(int i=1,t,pos,x;i<=m;i++) {
cin>>t;
if(t==2) {
if(Fuck[1].ans==inf) cout<<"-1"<<endl;
else cout<<Fuck[1].ans<<endl;
}
else {
cin>>pos>>x;
update(pos,x);
}
}
return 0;
}

感受

做了这道题,算是让我接受了线段树和二进制压位运算的洗礼吧。线段树真的是十分灵活的数据结果呀。

校内模拟赛T5:连续的“包含”子串长度( nekameleoni?) —— 线段树单点修改,区间查询 + 尺取法合并的更多相关文章

  1. 模拟赛 怨灵退治 题解(Hall定理+线段树)

    题意: 有 n 群怨灵排成一排,燐每秒钟会选择一段区间,消灭至多 k 只怨灵. 如果怨灵数量不足 k,则会消灭尽量多的怨灵. 燐作为一只有特点的猫,它选择的区间是不会相互包含的.它想要知道它每秒最多能 ...

  2. 【2019.7.26 NOIP模拟赛 T3】化学反应(reaction)(线段树优化建图+Tarjan缩点+拓扑排序)

    题意转化 考虑我们对于每一对激活关系建一条有向边,则对于每一个点,其答案就是其所能到达的点数. 于是,这个问题就被我们搬到了图上,成了一个图论题. 优化建图 考虑我们每次需要将一个区间向一个区间连边. ...

  3. 【2019.10.7 CCF-CSP-2019模拟赛 T2】绝对值(abs)(线段树细节题)

    找规律 设\(p_i=a_{i+1}-a_i\),则答案就是\(\sum_{i=1}^{n-1}p_i\). 考虑若将\(a_i\)加上\(x\)(边界情况特殊考虑),就相当于是将\(p_{i-1}\ ...

  4. 【CSP模拟赛】天才绅士少女助手克里斯蒂娜(线段树&读入优化&输出优化)

    题面描述 红莉栖想要弄清楚楼下天王寺大叔的显像管电视对“电话微波炉(暂定)”的影响.选取显像管的任意一个平面,一开始平面内有个n电子,初始速度分别为vi,定义飘升系数为 $$\sum_{1\leqsl ...

  5. 【BZOJ1396】识别子串 - 后缀自动机+线段树

    题意: Description Input 一行,一个由小写字母组成的字符串S,长度不超过10^5 Output L行,每行一个整数,第i行的数据表示关于S的第i个元素的最短识别子串有多长. 题解: ...

  6. 【20170521校内模拟赛】热爱生活的小Z

    学长FallDream所出的模拟赛,个人感觉题目难度还是比较适中的,难度在提高+左右,可能比较接近弱省省选,总体来讲试题考查范围较广,个人认为还是很不错的. 所有试题如无特殊声明,开启-O2优化,时限 ...

  7. BZOJ1396 识别子串 字符串 SAM 线段树

    原文链接http://www.cnblogs.com/zhouzhendong/p/9004467.html 题目传送门 - BZOJ1396 题意 给定一个字符串$s$,$|s|\leq 10^5$ ...

  8. BZOJ 1396: 识别子串( 后缀数组 + 线段树 )

    这道题各位大神好像都是用后缀自动机做的?.....蒟蒻就秀秀智商写一写后缀数组解法..... 求出Height数组后, 我们枚举每一位当做子串的开头. 如上图(x, y是height值), Heigh ...

  9. BZOJ 1396&&2865 识别子串[后缀自动机 线段树]

    Description 在这个问题中,给定一个字符串S,与一个整数K,定义S的子串T=S(i, j)是关于第K位的识别子串,满足以下两个条件: 1.i≤K≤j. 2.子串T只在S中出现过一次. 例如, ...

随机推荐

  1. 错排问题 && 洛谷 P1595 信封问题

    传送门 一道裸的错排问题 错排问题 百度百科上这样说 就是对于一个排列,每一个数都不在正确的位置上的方案数.n 个元素的错排数记为 D(n). 公式 D(n)=(n−1)∗(D(n−2)+D(n−1) ...

  2. 你所遵循的PEP8代码规范是什么?请举例说明其要求?

    1. 变量常量:大写加下划线 USER_CONSTANT.私有变量 : 小写和一个前导下划线 _private_value.Python 中不存在私有变量一说,若是遇到需要保护的变量,使用小写和一个前 ...

  3. weight (搜索对象的选取)

    #10249. 「一本通 1.3 例 5」weight [题目描述] 已知原数列 $a_1,a_2,\cdots,a_n$​​ 中的前 1项,前 2 项,前3项, $\cdots$ ,前 n 项的和, ...

  4. 2019牛客暑期多校训练营(第七场) - C - Governing sand - 平衡树

    5 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 感觉该出14才对,取前k小写成了取前k大. 5 1 5 4 2 5 3 3 5 2 4 5 1 6 5 5 suf=55 res=0 a ...

  5. addEventListener 的三个参数

    addEventListener 有三个参数:第一个参数表示事件名称(不含 on,如 "click"):第二个参数表示要接收事件处理的函数:第三个参数为 useCapture,本文 ...

  6. Could not load file or assembly system.data.sqlite.dll or one for it's depedencies

    最近做一个winform项目,因为数据库用的表很少,所以用的是轻量级数据库sqlite.sqlite的优点很多,但是他要分两个版本,32位或者64位,不能同时兼容. 我遇到的问题是,我在开发端用的是. ...

  7. Route的exact属性

    exact是Route下的一个属性,react路由会匹配到所有能匹配到的路由组件,exact能够使得路由的匹配更严格一些. exact的值为bool型,为true时表示严格匹配,为false时为正常匹 ...

  8. vim ctags

    ctags -I __THROW -I __attribute_pure__ -I __nonnull -I __attribute__ --file-scope=yes --langmap=c:+. ...

  9. 下载-MS SQL Server 2005(大全版)含开发人员版、企业版、标准版【转】

    中文名称:微软SQL Server 2005 英文名称:MS SQL Server 2005资源类型:ISO版本:开发人员版.企业版.标准版发行时间:2006年制作发行:微软公司地区:大陆语言:普通话 ...

  10. Apache Mesos1.0.1 编译安装部署教程(ubuntu)

    参考资料 官方文档:http://mesos.apache.org/documentation 中文翻译:http://mesos.mydoc.io/ GitHub:https://github.co ...