题目传送门:https://www.luogu.org/problemnew/show/3380
题目分析:为什么我要把这道模板题写在博客上呢?因为我想记录一个卡常的小技巧。骗访问量
这题我是考noip之前码的,写的是坐标线段树套动态开节点的权值线段树,虽然空间是 O(nlog2(n)) ,但实际空间并没有这么大。由于在 log(n) 棵线段树上同时二叉查找,时间是 O(mlog2(n)) 的,然而被卡成了70分,跑得比Ghastlcon的 log3(n) 的zkw线段树+二分+Treap还慢。
多次修改后还是T3个点,我决定弃坑了。今天早上我忽然想到一个很有力的优化:如果权值线段树已经走到空节点,就不要再往下走,直接退出。结果900多ms卡过了这题QAQ。
CODE:
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=50100;
const int maxl=21;
const int oo=2147483647;
struct Tnode
{
int sum;
Tnode *lson,*rson;
} tree[maxn*maxl*maxl];
Tnode *Seg[maxn<<2];
Tnode *Root[maxl<<1];
int cur=-1,num;
struct data
{
int l,r,k,val,id,Time,opt;
} work[maxn*3];
int temp;
int a[maxn];
int b[maxn<<1];
int n,m;
bool Comp1(data x,data y)
{
return x.val<y.val;
}
bool Comp2(data x,data y)
{
return x.Time<y.Time;
}
Tnode *New_node()
{
cur++;
tree[cur].sum=0;
tree[cur].lson=tree[cur].rson=tree;
return tree+cur;
}
void Build(int root,int L,int R)
{
Seg[root]=tree;
if (L==R) return;
int mid=(L+R)>>1;
int Left=root<<1;
int Right=Left|1;
Build(Left,L,mid);
Build(Right,mid+1,R);
}
int Query(Tnode *root,int L,int R,int x,int y)
{
if ( y<L || R<x || root==tree ) return 0;//root==tree的时候退出,这是个很有力的优化!!
if ( x<=L && R<=y ) return root->sum;
int mid=(L+R)>>1;
int vl=Query(root->lson,L,mid,x,y);
int vr=Query(root->rson,mid+1,R,x,y);
return (vl+vr);
}
int Ask(int root,int L,int R,int x,int y,int v)
{
if ( y<L || R<x ) return 0;
if ( x<=L && R<=y ) return Query(Seg[root],1,temp,1,v);
int mid=(L+R)>>1;
int Left=root<<1;
int Right=Left|1;
int vl=Ask(Left,L,mid,x,y,v);
int vr=Ask(Right,mid+1,R,x,y,v);
return (vl+vr);
}
void Push(int root,int L,int R,int x,int y)
{
if ( y<L || R<x ) return;
if ( x<=L && R<=y )
{
Root[++num]=Seg[root];
return;
}
int mid=(L+R)>>1;
int Left=root<<1;
int Right=Left|1;
Push(Left,L,mid,x,y);
Push(Right,mid+1,R,x,y);
}
int Find(int L,int R,int Rank)
{
if (L==R) return L;
int Left=0,mid=(L+R)>>1;
for (int i=1; i<=num; i++) Left+=Root[i]->lson->sum;
if (Rank<=Left)
{
for (int i=1; i<=num; i++) Root[i]=Root[i]->lson;
return Find(L,mid,Rank);
}
else
{
Rank-=Left;
for (int i=1; i<=num; i++) Root[i]=Root[i]->rson;
return Find(mid+1,R,Rank);
}
}
void Update(Tnode *&root,int L,int R,int x,int v)
{
if (root==tree) root=New_node();
if (L==R)
{
root->sum+=v;
return;
}
int mid=(L+R)>>1;
if (x<=mid) Update(root->lson,L,mid,x,v);
else Update(root->rson,mid+1,R,x,v);
root->sum=root->lson->sum+root->rson->sum;
}
void Insert(int root,int L,int R,int x,int v)
{
Update(Seg[root],1,temp,v,1);
if (L==R) return;
int mid=(L+R)>>1;
int Left=root<<1;
int Right=Left|1;
if (x<=mid) Insert(Left,L,mid,x,v);
else Insert(Right,mid+1,R,x,v);
}
void Delete(int root,int L,int R,int x,int v)
{
Update(Seg[root],1,temp,v,-1);
if (L==R) return;
int mid=(L+R)>>1;
int Left=root<<1;
int Right=Left|1;
if (x<=mid) Delete(Left,L,mid,x,v);
else Delete(Right,mid+1,R,x,v);
}
int main()
{
freopen("3380.in","r",stdin);
freopen("3380.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1; i<=n; i++)
{
scanf("%d",&a[i]);
work[i].val=a[i];
work[i].l=i;
work[i].opt=3;
}
int x=n;
for (int i=1; i<=m; i++)
{
x++;
scanf("%d",&work[x].opt);
if (work[x].opt!=3)
{
scanf("%d%d",&work[x].l,&work[x].r);
if (work[x].opt==2) scanf("%d",&work[x].k);
else scanf("%d",&work[x].val);
}
else
{
work[x].opt=6;
scanf("%d",&work[x].l);
work[x].val=a[ work[x].l ];
x++;
work[x].opt=3;
scanf("%d",&work[x].val);
work[x].l=work[x-1].l;
a[ work[x].l ]=work[x].val;
}
}
for (int i=1; i<=x; i++) work[i].Time=i;
sort(work+1,work+x+1,Comp1);
temp=work[1].id=1;
for (int i=2; i<=x; i++)
if (work[i-1].val==work[i].val) work[i].id=temp;
else work[i].id=++temp,b[temp]=work[i].val;
sort(work+1,work+x+1,Comp2);
New_node();
Build(1,1,n);
for (int i=1; i<=x; i++)
{
if (work[i].opt==1)
{
int ans=Ask(1,1,n,work[i].l,work[i].r,work[i].id-1)+1;
printf("%d\n",ans);
}
if (work[i].opt==2)
{
num=0;
Push(1,1,n,work[i].l,work[i].r);
int ans=Find(1,temp,work[i].k);
printf("%d\n",b[ans]);
}
if (work[i].opt==3) Insert(1,1,n,work[i].l,work[i].id);
if (work[i].opt==4)
{
int ans;
int Rank=Ask(1,1,n,work[i].l,work[i].r,work[i].id-1);
if (!Rank) ans=-oo;
else
{
num=0;
Push(1,1,n,work[i].l,work[i].r);
ans=b[ Find(1,temp,Rank) ];
}
printf("%d\n",ans);
}
if (work[i].opt==5)
{
int ans;
int Rank=Ask(1,1,n,work[i].l,work[i].r,work[i].id);
if (Rank==work[i].r-work[i].l+1) ans=oo;
else
{
Rank++;
num=0;
Push(1,1,n,work[i].l,work[i].r);
ans=b[ Find(1,temp,Rank) ];
}
printf("%d\n",ans);
}
if (work[i].opt==6) Delete(1,1,n,work[i].l,work[i].id);
}
//printf("%d\n",sizeof(tree)>>20);
return 0;
}