3065: 带插入区间K小值
Time Limit: 60 Sec Memory Limit: 512 MB
Submit: 2627 Solved: 784
[Submit][Status][Discuss]
Description
从前有n只跳蚤排成一行做早操,每只跳蚤都有自己的一个弹跳力a[i]。跳蚤国王看着这些跳蚤国欣欣向荣的情景,感到非常高兴。这时跳蚤国王决定理性愉悦一下,查询区间k小值。他每次向它的随从伏特提出这样的问题: 从左往右第x个到第y个跳蚤中,a[i]第k小的值是多少。
这可难不倒伏特,他在脑袋里使用函数式线段树前缀和的方法水掉了跳蚤国王的询问。
这时伏特发现有些跳蚤跳久了弹跳力会有变化,有的会增大,有的会减少。
这可难不倒伏特,他在脑袋里使用树状数组套线段树的方法水掉了跳蚤国王的询问。(orz 主席树)
这时伏特发现有些迟到的跳蚤会插入到这一行的某个位置上,他感到非常生气,因为……他不会做了。
请你帮一帮伏特吧。
快捷版题意:带插入、修改的区间k小值在线查询。
Input
第一行一个正整数n,表示原来有n只跳蚤排成一行做早操。
第二行有n个用空格隔开的非负整数,从左至右代表每只跳蚤的弹跳力。
第三行一个正整数q,表示下面有多少个操作。
下面一共q行,一共三种操作对原序列的操作:(假设此时一共m只跳蚤)
1. Q x y k: 询问从左至右第x只跳蚤到从左至右第y只跳蚤中,弹跳力第k小的跳蚤的弹跳力是多少。
(1 <= x <= y <= m, 1 <= k <= y - x + 1)
2. M x val: 将从左至右第x只跳蚤的弹跳力改为val。
(1 <= x <= m)
3. I x val: 在从左至右第x只跳蚤的前面插入一只弹跳力为val的跳蚤。即插入后从左至右第x只跳蚤是我刚插入的跳蚤。
(1 <= x <= m + 1)
为了体现在线操作,设lastAns为上一次查询的时候程序输出的结果,如果之前没有查询过,则lastAns = 0。
则输入的时候实际是:
Q _x _y _k ——> 表示 Q _x^lastAns _y^lastAns _k^lastAns
M _x _val ——> 表示 M _x^lastAns _val^lastAns
I _x _val ——> 表示 I _x^lastAns _val^lastAns
简单来说就是操作中输入的整数都要异或上一次询问的结果进行解码。
(祝Pascal的同学早日转C++,就不提供pascal版的描述了。)
Output
对于每个询问输出回答,每行一个回答。
Sample Input
10
10 5 8 28 0 19 2 31 1 22
30
I 6 9
M 1 11
I 8 17
M 1 31
M 6 26
Q 2 7 6
I 23 30
M 31 7
I 22 27
M 26 18
Q 26 17 31
I 5 2
I 18 13
Q 3 3 3
I 27 19
Q 23 23 30
Q 5 13 5
I 3 0
M 15 27
Q 0 28 13
Q 3 29 11
M 2 8
Q 12 5 7
I 30 19
M 11 19
Q 17 8 29
M 29 4
Q 3 0 12
I 7 18
M 29 27
Sample Output
28
2
31
0
14
15
14
27
15
14
HINT
此题作为一个小小的研究来搞吧~做法有很多~不知道这题究竟有多少种做法。
请自觉O(log^2n),我故意卡块状链表,块链A了的请受我深情一拜……
A掉的同学请在Discuss里面简要说下自己的做法吧~
原序列长度 <= 35000
插入个数 <= 35000,修改个数 <= 70000,查询个数 <= 70000 ,0 <= 每时每刻的权值 <= 70000
由于是OJ上的题,所以数据无梯度。为了防止卡OJ,本题只有4组数据。
【分析】
本蒟蒻做分块算法的第一题。由于实在不会花式线段树,只能通过这种半暴力的方式来做。代码借鉴swm_sxt,在此表示感谢。
A题无力啊啊啊。
对于本题,将所有数分成num个连续区间,除最后一个区间外每个区间中存放600个元素。对每个区间进行排序处理,当然也要保留原来未排序的区间,当查询l~r之间的第k小值时,我们可以二分答案(权值<=70000)。对于被完全覆盖的块,可以进行二分查找,未完全覆盖的块,进行暴力查找。
当改变元素时,暴力找出元素位置,然后对于元素所在区间插入排序即可(当然也可以用二分)。
当添加元素时,如果添加的元素所在区间元素个数≥2b,即1200时,要对区间进行“分裂”,将区间个数num++,然后把后600个元素添加进num区间,前600个添加进原区间。然后用后继表示法:假设所在区间为i,那么 next[num]=next[i],next[i]=num 即可。
总复杂度O(m*log²n)
【代码】
//bzoj 3065 带插入区间K小值
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,b,x,l,r,num,ans,lc,rc,li,ri,v;
char ch[3];
int ne[201],s[201],k[201][1205],a[201][1205];
inline int que(int i,int e)
{
int ll=0,rr=s[i]-1,mid;
while(ll<rr)
{
mid=(ll+rr+1)/2;
if(k[i][mid]>=e) rr=mid-1;
else ll=mid;
}
if(k[i][ll]<e) ll++;
return ll;
}
inline int ask()
{
scanf("%d%d%d",&l,&r,&x);
l^=ans,r^=ans,x^=ans;
lc=rc=0;
//找出l,r所在区间及所在的位置
for(li=0;li!=-1;li=ne[li])
if((lc=lc+s[li])>=l) break;
lc=l-lc+s[li]-1;
for(ri=0;ri!=-1;ri=ne[ri])
if((rc=rc+s[ri])>=r) break;
rc=r-rc+s[ri]-1;
//二分答案
int ll=0,rr=70000,mid,t;
while(ll<rr)
{
mid=(ll+rr+1)/2,t=0;
if(li!=ri) //不在同一个块
{
for(int i=ne[li];i!=ri;i=ne[i])
t+=que(i,mid); //找出被区间完全覆盖的块中的答案
for(int i=lc;i<s[li];i++) //暴力查询部分覆盖区间
if(a[li][i]<mid) t++;
for(int i=0;i<=rc;i++)
if(a[ri][i]<mid) t++;
}
else
for(int i=lc;i<=rc;i++)
if(a[li][i]<mid) t++;
if(t>=x) rr=mid-1;
else ll=mid;
}
return ll;
}
inline void change() //暴力修改
{
scanf("%d%d",&x,&v);
x^=ans,v^=ans;
lc=0;int i,j;
for(i=0;i!=-1;i=ne[i])
if((lc=lc+s[i])>=x) break;
lc=x-lc+s[i]-1;
for(j=0;j<s[i];j++)
if(k[i][j]==a[i][lc]) break;
k[i][j]=a[i][lc]=v;
while(j && k[i][j]<k[i][j-1])
swap(k[i][j],k[i][j-1]),j--;
while(j<s[i]-1 && k[i][j]>k[i][j+1])
swap(k[i][j],k[i][j+1]),j++;
}
inline void in()
{
scanf("%d%d",&x,&v);
x^=ans,v^=ans;
lc=0;
int j;
for(li=0;li!=-1;li=ne[li])
{
lc+=s[li];
if(lc>=x || ne[li]==-1) break;
}
x=x-lc+s[li]-1;
for(j=s[li];j>x;j--) a[li][j]=a[li][j-1];
s[li]++;a[li][x]=v;
if(s[li]==2*b)
{
num++;
s[num]=b;
s[li]=b;
ne[num]=ne[li];
ne[li]=num;
for(j=0;j<b;j++)
k[num][j]=a[num][j]=a[li][b+j],
k[li][j]=a[li][j];
sort(k[li],k[li]+b);
sort(k[num],k[num]+b);
}
else
{
for(j=0;j<s[li]-1;j++)
if(v<k[li][j]) break;
int tmp=j;
for(j=s[li]-1;j>tmp;j--)
k[li][j]=k[li][j-1];
k[li][tmp]=v;
}
}
int main()
{
scanf("%d",&n);
b=600;
for(int i=0;i<n;i++)
{
scanf("%d",&x);
a[i/b][i%b]=k[i/b][i%b]=x;
}
num=(n+b-1)/b-1;
for(int i=0;i<num;i++)
ne[i]=i+1,s[i]=b;
ne[num]=-1;
s[num]=n-(b*num);
for(int i=0;i<=num;i++)
sort(k[i],k[i]+s[i]);
scanf("%d",&m);
while(m--)
{
scanf("%s",ch);
if(ch[0]=='Q')
printf("%d\n",ans=ask());
else if(ch[0]=='M') change();
else in();
}
return 0;
}