Description
Jry最近做(屠)了很多数据结构题,所以想 BS你,他希望你能实现一种数据结构维护一个序列:
Input
第一行n;
第二行n个数;
第三行q,代表询问个数;
接下来q行,每行一个op,输入格式见描述。
Output
对于7≤op≤11的操作,一行输出一个答案。
Sample Input
6
5 2 6 3 1 4
15
7 2 4
8 1 3
9 2 4 5
10 1 6 4
11 2 5 4
6 1 4 7
8 1 4
5 3 4 5
2 1
1 2 8
3 3 5
4 1 5 2
9 2 5 4
10 3 6 4
11 1 6 100
Sample Output
11
4
1
4
3
0
3
12
6
HINT
n,q≤100000;
任意时刻数列中的数≤2^31-1。
0≤任意时刻数列中的数≤2^31-1。
本题共3组数据
题解
自从做了这题,感觉世界豁然开朗了。。
感觉自己再也不怂数据结构题了
感觉自己的块状链表打开了新世界
感觉维修数列已经不再是恐怖的数据结构题了
感觉自己的码力+=MAX
全程%下面的大佬
ORZ
必须要怒%
由于抄代码,也就写了一下午就写完了
该注意的细节注释里都有了
怎么做注释里也有了
询问操作的注释可能比较少,但是相信大家也能看懂
就这样吧
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#include<iostream>
#include<cmath>
using namespace std;
typedef long long LL;
const int N=10000;
const int MAX=1<<29;
int n,m;
int size;//块的大小
struct node
{
int d[1003],s[1003];//原数组 排序后的数组
int rev,add,same,size,next;//翻转标记 区间加 区间一样 这个块的大小 链表
LL sum;//区间和
}a[N];//块
queue<int> q;//回收节点
void Init ()
{
for (int u=1;u<N;u++) q.push(u);
a[0].next=-1;a[0].size=0;
}
int new_node()
{
int temp=q.front();q.pop();
return temp;
}
void clear (int x){a[x].rev=a[x].add=a[x].same=a[x].size=a[x].rev=0;}
void del (int x){q.push(x);clear(x);}//删除并回收这个块
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;
}
void find (int &pos,int &now)//找到pos所在的块与排位
{
for (now=0;a[now].next!=-1&&pos>a[now].size;now=a[now].next)
pos-=a[now].size;
}
void push_down(int now)//将now这个块的标记都下传
{
int tot=a[now].size;//先得到这个块的大小
if (a[now].rev)
{
a[now].rev=false;
int tt=(tot>>1);
for (int u=1;u<=tt;u++) swap(a[now].d[u],a[now].d[tot-u+1]);
}
if (a[now].same!=0)
{
for (int u=1;u<=tot;u++)
a[now].d[u]=a[now].same;
a[now].sum=a[now].same*tot;
a[now].same=0;
}
if (a[now].add!=0)
{
for (int u=1;u<=tot;u++)
a[now].d[u]+=a[now].add;
a[now].sum=a[now].sum+a[now].add*tot;
a[now].add=0;
}
}
void update (int x)//重构这个块
{
a[x].sum=0;
for (int u=1;u<=a[x].size;u++)
a[x].sum+=a[x].d[u],a[x].s[u]=a[x].d[u];
sort(a[x].s+1,a[x].s+a[x].size+1);
}
void spilt (int now,int pos)//将now这一个块的后pos个分离出来
{
push_down(now);//因为要把分离,所以要下传了
int t=new_node();//将这个块的pos到后面分裂
for (int u=pos;u<=a[now].size;u++)
a[t].d[++a[t].size]=a[now].d[u];
a[t].next=a[now].next;
a[now].next=t;
a[now].size=max(pos-1,0);//把后面的一部分裁掉
update(t);update(now);//其实这里看起来是2sqrtn,但他们加起来其实只有sqrtn的
}
void Merge (int now)//将now和now+1合并
{
int k=a[now].next;
push_down(now);push_down(k);
for (int u=1;u<=a[k].size;u++)
a[now].d[++a[now].size]=a[k].d[u];
a[now].next=a[k].next;del(k);
update(now);
}
void maintain(int now)//看下后面有没有可以合并的
{
for (;now!=-1;now=a[now].next)
if (a[now].next!=-1&&a[now].size+a[a[now].next].size<=size)//这两个小块,合并掉
Merge(now);
}
void ins (int pos,int x)//在pos这个位置后面插入一个值为x的点
{
int now;pos++;
find(pos,now);spilt(now,pos);
a[now].d[++a[now].size]=x;//直接插到末尾就可以了
/*插入排序*/
a[now].sum+=x;
int lalal;//寻找放在哪里
for (lalal=1;lalal<a[now].size;lalal++)
if (a[now].s[lalal]>x) break;
for (int u=a[now].size;u>lalal;u--)
a[now].s[u]=a[now].s[u-1];
a[now].s[lalal]=x;
/*插入排序*/
/*注意:这里为什么可以写插入排序呢?是因为上面的spi操作已经把s数组更新了,都得到了标记的值*/
/*但是下面del操作的时候s数组是没有更新的,因此要重构*/
maintain(now);
}
void Del (int pos)//删除第x个数
{
int now;
find(pos,now);push_down(now);
for (int u=pos+1;u<=a[now].size;u++)
a[now].d[u-1]=a[now].d[u];
a[now].size--;
update(now);
maintain(now);
}
void solve (int l,int r,int &lp,int &rp)//把这段区间分离出来
{
int pos=l;
find(pos,lp);spilt(lp,pos);
pos=r+1;
find(pos,rp);
spilt(rp,pos);
pos=r;
find(pos,rp);
}
int st[N];
void do_reverse (int l,int r)//翻转操作
{
/*翻转过程怎么做呢:*/
/*先把区间都分离出来,然后把他们间的顺序弄反,其实就是链表翻转*/
/*然后要注意头和尾的链表*/
/*然后每个块里面翻转*/
int lp,rp;
solve(l,r,lp,rp);
int now=lp,top=0;
for (int u=a[lp].next;u!=a[rp].next;u=a[u].next)
st[++top]=u,a[u].rev^=1;
a[st[1]].next=a[rp].next;
for (int u=top;u>1;u--)
a[st[u]].next=st[u-1];
a[lp].next=rp;
maintain(lp);
}
void do_move(int l,int r,int k)
{
int lp,mp,rp,np;
solve(l,r-k,lp,mp);
solve(r-k+1,r,mp,rp);
np=a[lp].next;//np才是l在的块
a[lp].next=a[mp].next;
a[mp].next=a[rp].next;
a[rp].next=np;
maintain(lp);
}
void add (int l,int r,int val)
{
int lp,rp;
solve(l,r,lp,rp);
for (int now=a[lp].next;now!=a[rp].next;now=a[now].next)
{
a[now].add+=val;
a[now].sum=a[now].sum+a[now].size*val;
}
maintain(lp);
}
void same (int l,int r,int val)
{
int lp,rp;
solve(l,r,lp,rp);
for (int now=a[lp].next;now!=a[rp].next;now=a[now].next)
{
a[now].add=0;
a[now].same=val;
a[now].sum=a[now].size*val;
}
maintain(lp);
}
LL get (int l,int r)//这一段的和
{
int lp,rp;
solve(l,r,lp,rp);
LL ans=0;
for (int now=a[lp].next;now!=a[rp].next;now=a[now].next)
ans=ans+a[now].sum;
maintain(lp);
return ans;
}
int get1 (int l,int r)//这一段的最大值减最小值
{
int lp,rp;
solve(l,r,lp,rp);
int maxx=-MAX,minn=MAX;
for (int now=a[lp].next;now!=a[rp].next;now=a[now].next)
if (a[now].size!=0)
{
if (a[now].same!=0)//注意,一定要判这一句 虽然看起来solve里面有update,但中间的块都是没有update的
{
minn=min(minn,a[now].same+a[now].add);
maxx=max(maxx,a[now].same+a[now].add);
}
else
{
minn=min(minn,a[now].s[1]+a[now].add);
maxx=max(maxx,a[now].s[a[now].size]+a[now].add);
}
}
maintain(lp);
return maxx-minn;
}
int near (int l,int r,int val)
{
int lp,rp;
solve(l,r,lp,rp);
int ans=MAX;
for (int now=a[lp].next;now!=a[rp].next;now=a[now].next)
{
if (a[now].same)
ans=min(ans,abs(val-a[now].same-a[now].add));
else
{
int id=lower_bound(a[now].s+1,a[now].s+a[now].size+1,val-a[now].add)-a[now].s;
if (id!=a[now].size+1)
ans=min(ans,a[now].s[id]+a[now].add-val);
if (id!=1)
id--,ans=min(ans,val-a[now].s[id]-a[now].add);
}
}
maintain(lp);
return ans;
}
int ask_mink(int l,int r,int k)//l到r的第k小
{
int lp,rp;
solve(l,r,lp,rp);
int ll=0,rr=MAX;
while (ll<rr)
{
int mid=(ll+rr)/2+1;
int sum=1;//看看有多少个数
for (int now=a[lp].next;now!=a[rp].next;now=a[now].next)
{
if (a[now].same!=0)
{
if (a[now].same+a[now].add<mid)
sum=sum+a[now].size;
}
else
{
int id=upper_bound(a[now].s+1,a[now].s+a[now].size+1,mid-a[now].add-1)-a[now].s;
sum=sum+max(0,id-1);
}
}
if (k>=sum) ll=mid;
else rr=mid-1;
}
maintain(lp);
return ll;
}
int ask_smaller(int l,int r,int val)
{
int lp,rp;
solve(l,r,lp,rp);
int ans=0;
for (int now=a[lp].next;now!=a[rp].next;now=a[now].next)
{
if (a[now].same!=0)
{
if (a[now].same+a[now].add<val)
ans=ans+a[now].size;
}
else
{
int it=upper_bound(a[now].s+1,a[now].s+a[now].size+1,val-a[now].add-1)-a[now].s;
ans=ans+it-1;
}
}
maintain(lp);
return ans;
}
int main()
{
n=read();
size=sqrt(n);Init();
for (int u=1;u<=n;u++)
{
int x=read();
ins(u-1,x);
}
m=read();
for (int u=1;u<=m;u++)
{
int op,x,y,z;op=read();
switch(op)
{
case 1:x=read();y=read();ins(x,y);break;
case 2:x=read();Del(x);break;
case 3:x=read();y=read();do_reverse(x,y);break;
case 4:x=read();y=read();z=read();do_move(x,y,z);break;
case 5:x=read();y=read();z=read();add(x,y,z);break;
case 6:x=read();y=read();z=read();same(x,y,z);break;
case 7:x=read();y=read();printf("%lld\n",get(x,y));break;
case 8:x=read();y=read();printf("%d\n",get1(x,y));break;
case 9:x=read();y=read();z=read();printf("%d\n",near(x,y,z));break;
case 10:x=read();y=read();z=read();printf("%d\n",ask_mink(x,y,z));break;
case 11:x=read();y=read();z=read();printf("%d\n",ask_smaller(x,y,z));break;
}
}
return 0;
}