
本文共 4584 字,大约阅读时间需要 15 分钟。
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组数据。
题解
我写的是分块。。
树套树什么的不会啊
话说都高一还不怎么会树套树
CODE:
#include<cstdio>#include<algorithm>#include<cstring>#include<iostream>using namespace std;int b,num;//每个块的大小 有多少个块 int n,m;int k[305][2005];//排序后的数组 int a[305][2005];//原来的数组int ne[305];//这个块的下一个块是哪一个int s[305];//这个块有多少个人int last_ans;inline int read(){ int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){ x=x*10+ch-'0';ch=getchar();} return x*f;}int que (int i,int x)//这个块小于x的有多少个 { int l=0,r=s[i]-1; int ans=0; while (l<=r) { int mid=(l+r)>>1; if (k[i][mid]<x) {ans=mid;l=mid+1;} else r=mid-1; } if (k[i][ans]<x) ans++; return ans;}int ask (){ int l=read()^last_ans,r=read()^last_ans,x=read()^last_ans; int lc=0,rc=0,li,ri;//这个块的第几个元素 在哪一块 for (li=0;li!=-1;li=ne[li]) { lc+=s[li]; if (lc>=l) break; } lc=l-lc+s[li]-1; for (ri=0;ri!=-1;ri=ne[ri]) { rc+=s[ri]; if (rc>=r) break; } rc=r-rc+s[ri]-1; int ll=0,rr=70000,ans; while (ll<=rr) { int mid=(ll+rr)>>1,nu=0; if (li!=ri)//他们不在一个块里面 { for (int u=ne[li];u!=ri;u=ne[u]) nu+=que(u,mid); for (int u=lc;u<s[li];u++) if (a[li][u]<mid) nu++; for (int u=0;u<=rc;u++) if (a[ri][u]<mid) nu++; } else for (int u=lc;u<=rc;u++) if (a[li][u]<mid) nu++; // printf("%d %d %d\n",ll,rr,nu); if (nu<x) {ans=mid;ll=mid+1;} else rr=mid-1; } return ans;}void change (){ int x=read()^last_ans,c=read()^last_ans; int l=0,u,i; for (u=0;u!=-1;u=ne[u]) if ((l+=s[u])>=x) break; x=x-l+s[u]-1; for (i=0;i<s[u];i++) if (k[u][i]==a[u][x]) break; k[u][i]=a[u][x]=c; while (i>0&&k[u][i]<k[u][i-1]) swap(k[u][i],k[u][i-1]),i--; while (i<s[u]-1&&k[u][i]>k[u][i+1]) swap(k[u][i],k[u][i+1]),i++;}void In (){ int x=read()^last_ans,c=read()^last_ans; int l=0,i,j; for (i=0;i!=-1;i=ne[i]) { l+=s[i]; if (l>=x||ne[i]==-1) break; } x=x-l+s[i]-1; for (j=s[i];j>x;j--) a[i][j]=a[i][j-1]; s[i]++;a[i][x]=c; if (s[i]==2*b) { s[num]=b;s[i]=b; ne[num]=ne[i]; ne[i]=num; for (j=0;j<b;j++) k[num][j]=a[num][j]=a[i][b+j],k[i][j]=a[i][j]; sort(k[i],k[i]+b);sort(k[num],k[num]+b); num++; } else { for (j=0;j<s[i]-1;j++) if (c<k[i][j]) break; l=j; for (j=s[i]-1;j>=l;j--) k[i][j]=k[i][j-1]; k[i][l]=c; }}int main(){ n=read();b=600; num=(n+b-1)/b; for (int u=0;u<n;u++) k[u/b][u%b]=a[u/b][u%b]=read(); for (int u=0;u<num-1;u++) s[u]=b,ne[u]=u+1;s[num-1]=n-b*(num-1);ne[num-1]=-1; for (int u=0;u<num;u++) sort(k[u],k[u]+s[u]); m=read();last_ans=0; while (m--) { char ss[5]; scanf("%s",ss); if (ss[0]=='Q') { last_ans=ask(); printf("%d\n",last_ans); } else if (ss[0]=='M') change(); else In(); } return 0;}
发表评论
最新留言
关于作者
