劳资就是想单纯的测个模板,怎么就特喵的这么难。
首先说一下自己对主席树的理解。
作用:确定区间[L,R]内的第K大数,或者区间内有多少个不同个元素。
主席树的另一个名字叫做函数式线段树,然后我查了一下函数式的特点,其中之一就是不修改状态。
从主席树上来看这个特点就是:在向主席树插入第 i 个元素之后,我们仍能查询到没插入 i 之前的状态。
主席树的构造:
对于有n个元素,s个不同元素的区间,对应的主席树CT 是由n 棵线段树组成的 ,对于第 i 棵线段树记录[1,i]区间内s个元素出现的次数。
那么当查询[L,R](1<= L <= R <= n,1 <= k <= R-L+1)内第K大数,我们需要讨论[1,L-1] ,[1,R]内各个元素的出现次数之差。
1.最开始时l = 1,r = s,;
2.mid = (l+r)>>1,如果[1,L-1],[1,R] 两者的[l,mid]的出现之差大于等于k,答案肯定在[l,mid],否则肯定在[mid+1,r]。
3.更新l,r,如果l == r,算法结束,答案就是s个元素中的第 r 大的数。否则转步骤二。
一些优化:
如果我们对于每个节点都要去建立一棵线段树,那么复杂度为n*s*log(s)。
但是分析一下会发现第i-1棵与第 i 棵线段树只会有log(s)个元素不一样,也就是第 i 个元素插入时造成的差异,那么我们只需要新开辟log(s)个节点,其它节点第i-1棵与第 i 棵线段树共用就好了。这样就降到了n*log(s)。
但是这样是没有办法进行高效的更新的。
很幸运的是我们一直在用区间作差的方法来查询结果,那么一个很显然的想法就是在ChairTree外面套一层BIT,使得一次查询和更新的复杂度都到了log(n)*log(s)。
空间复杂度升到了(n+m)*log(n)*log(s)。
然后这个题就解决了。
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <queue>
#include <cmath>
#include <algorithm>
#include <string>
#define LL long long
#define EPS (1e-8)
#define INF 0x3f3f3f
using namespace std;
const int MAXQ = 10010;
const int MAXN = 50010;
const int MAXLOG = 16;
int num[MAXN],uni[MAXN+MAXQ];
int siz;
inline int lowbit(int x)
{
return x&(-x);
}
int DelSame(int *num,int n)
{
sort(uni+1,uni+n+1);
int i,j;
for(j = 1,i = 2; i <= n; ++i)
if(num[i] != num[j])
num[++j] = num[i];
return j;
}
struct ChairTree
{
int l,r;
int ans;
} ct[MAXQ*MAXLOG*MAXLOG];
int ctRootofBIT[MAXN];
int ctRoot[MAXN];
int ctTop;
int LIndex[MAXLOG],RIndex[MAXLOG];
int CalSite(int x,int L,int R,int *uni)
{
int mid;
while(L <= R)
{
mid = (L+R)>>1;
if(x == uni[mid])
return mid;
if(x < uni[mid])
R = mid-1;
else
L = mid+1;
}
return -1;
}
int InitZeroLayer(int l,int r)
{
if(l == r)
{
ct[ctTop].l = -1,ct[ctTop].r = -1;
ct[ctTop].ans = 0;
return ctTop++;
}
int mid = (l+r)>>1;
int root = ctTop++;
ct[root].ans = 0;
ct[root].l = InitZeroLayer(l,mid);
ct[root].r = InitZeroLayer(mid+1,r);
return root;
}
int InitIthLayer(int pre,int l,int r,int goal)
{
if(l == r)
{
ct[ctTop].l = -1,ct[ctTop].r = -1;
ct[ctTop].ans = ct[pre].ans+1;
return ctTop++;
}
int mid = (l+r)>>1,root = ctTop++;
ct[root] = ct[pre];
if(goal <= mid)
ct[root].l = InitIthLayer(ct[pre].l,l,mid,goal);
else
ct[root].r = InitIthLayer(ct[pre].r,mid+1,r,goal);
ct[root].ans = ct[ct[root].l].ans + ct[ct[root].r].ans;
return root;
}
void InitChairTree(int n,int siz)
{
ctTop = 0;
ctRoot[0] = InitZeroLayer(1,siz);
int i,tmp;
for(i = 1; i <= n; ++i)
ctRoot[i] = InitIthLayer(ctRoot[i-1],1,siz,tmp = CalSite(num[i],1,siz,uni));
}
int QueryOnChairTree(int L,int R,int k,int l,int r)
{
if(l == r)
return uni[r];
int mid = (l+r)>>1;
if(ct[ct[R].l].ans - ct[ct[L].l].ans >= k)
return QueryOnChairTree(ct[L].l,ct[R].l,k,l,mid);
return QueryOnChairTree(ct[L].r,ct[R].r,k-(ct[ct[R].l].ans - ct[ct[L].l].ans),mid+1,r);
}
void InitChairTreeInBIT(int n)
{
memset(ctRootofBIT,-1,sizeof(int)*(n+2));
}
void UpdateIthLayerCTInBIT(int &root,int L,int R,int k,int data)
{
if(root == -1)
{
ct[ctTop].l = -1;
ct[ctTop].r = -1;
ct[ctTop].ans = 0;
root = ctTop++;
}
if(L == R)
{
ct[root].ans += data;
return ;
}
int mid = (L+R)>>1;
if(k <= mid)
UpdateIthLayerCTInBIT(ct[root].l,L,mid,k,data);
else
UpdateIthLayerCTInBIT(ct[root].r,mid+1,R,k,data);
ct[root].ans = 0;
if(ct[root].l != -1) ct[root].ans += ct[ct[root].l].ans;
if(ct[root].r != -1) ct[root].ans += ct[ct[root].r].ans;
}
void UpdateChairTreeInBIT(int site,int data,int n)
{
int tr = site,k = CalSite(num[site],1,siz,uni);
while(tr <= n)
{
UpdateIthLayerCTInBIT(ctRootofBIT[tr],1,siz,k,-1);
tr += lowbit(tr);
}
num[site] = data;
tr = site,k = CalSite(data,1,siz,uni);
while(tr <= n)
{
UpdateIthLayerCTInBIT(ctRootofBIT[tr],1,siz,k,1);
tr += lowbit(tr);
}
}
int GiveMetheAnwser(int LTop,int RTop,int Lroot,int Rroot,int k,int L,int R)
{
if(L == R) return uni[L];
int mid = (L+R)>>1,i,tr,tl,ans = ct[ct[Rroot].l].ans-ct[ct[Lroot].l].ans;
for(i = 0; i < RTop; ++i)
if(ct[RIndex[i]].l != -1) ans += ct[ct[RIndex[i]].l].ans;
for(i = 0; i < LTop; ++i)
if(ct[LIndex[i]].l != -1) ans -= ct[ct[LIndex[i]].l].ans;
if(k <= ans)
{
for(i = 0,tr = 0; i < RTop; ++i)
if(ct[RIndex[i]].l != -1) RIndex[tr++] = ct[RIndex[i]].l;
for(i = 0,tl = 0; i < LTop; ++i)
if(ct[LIndex[i]].l != -1) LIndex[tl++] = ct[LIndex[i]].l;
return GiveMetheAnwser(tl,tr,ct[Lroot].l,ct[Rroot].l,k,L,mid);
}
for(i = 0,tr = 0; i < RTop; ++i)
if(ct[RIndex[i]].r != -1) RIndex[tr++] = ct[RIndex[i]].r;
for(i = 0,tl = 0; i < LTop; ++i)
if(ct[LIndex[i]].r != -1) LIndex[tl++] = ct[LIndex[i]].r;
return GiveMetheAnwser(tl,tr,ct[Lroot].r,ct[Rroot].r,k-ans,mid+1,R);
}
int QueryKthEleOnCTandCTInBIT(int L,int R,int k,int n)
{
int LTop = 0,RTop = 0,tmp;
tmp = L;
while(tmp)
{
if(ctRootofBIT[tmp] != -1)
LIndex[LTop++] = ctRootofBIT[tmp];
tmp -= lowbit(tmp);
}
tmp = R;
while(tmp)
{
if(ctRootofBIT[tmp] != -1)
RIndex[RTop++] = ctRootofBIT[tmp];
tmp -= lowbit(tmp);
}
return GiveMetheAnwser(LTop,RTop,ctRoot[L],ctRoot[R],k,1,siz);
}
struct OP
{
int l,r,k;
}op[MAXQ];
int main()
{
int i,j,n,m;
int T;
scanf("%d",&T);
char ty[2];
while(T--)
{
scanf("%d %d",&n,&m);
for(i = 1; i <= n; ++i)
scanf("%d",&num[i]);
memcpy(uni,num,sizeof(num));
for(i = 1,j = 0; i <= m; ++i)
{
scanf("%s",ty);
if(ty[0] == 'Q')
scanf("%d %d %d",&op[i].l,&op[i].r,&op[i].k);
else
scanf("%d %d",&op[i].l,&op[i].r),op[i].k = -1,uni[n+(++j)] = op[i].r;
}
siz = DelSame(uni,n+j);
InitChairTree(n,siz);
InitChairTreeInBIT(n);
for(i = 1; i <= m; ++i)
{
if(op[i].k == -1)
UpdateChairTreeInBIT(op[i].l,op[i].r,n);
else
printf("%d\n",QueryKthEleOnCTandCTInBIT(op[i].l-1,op[i].r,op[i].k,n));
}
}
return 0;
}