静态区间第k小问题,是给你一个序列,每次询问序列中的一个区间中的第k小数,这个问题用普通的主席树就可以解决。动态区间第k小问题就是在静态的基础上加上了修改操作,也就是每次除了询问区间第k小之外,还可以修改序列中的某个数。因为这里涉及到了修改操作,我们用只用主席树好像难以完成这个问题,下面我们简单来分析一下这个问题。
我们知道在主席树中,第i棵线段树T[i]维护的是序列[1,i]中的数,我们设想一下,如果我们修改一个数,那么这个修改将影响到的线段树有T[i], T[i + 1] … ..T[n],如果我们用普通暴力的方法,每次都更新所有的这些线段树,那么时间复杂度爆表,这种做法肯定是不可行的。那么我们要想另外一种比O(n)更新还快的操作来解决这个问题。我们分析主席树的性质可以得到,如果修改了一个树,对那些线段树的影响都是一样的,比如,序列中原来位置i上的数是x,现在我们把它修改成y,那么对于需要修改的线段树,T[i], T[i + 1]…….T[n],每棵线段树的影响都是对应线段树上减去了数x,加上了数y,到了这里我们可以联想到树状数组,好像可以优化这个操作。我们让树状数组上的每个点对应一棵线段树,第i棵线段树维护区间[i - lowbit(i) + 1, i]区间的变化量就行。
下面是zoj2212的ac代码(第一次写数据结构套数据结构,写的比较丑):
#include
using namespace std;
const int maxn = 6e4 + 10;
int n, q;
struct node{
int L, R;
int sum;
node(){
sum = 0;
}
}Tree[2500010];//线段树的节点
int cnt;
int s[maxn];//树状数组,每个点表示一颗线段树的根节点
int X[maxn], Y[maxn];//辅助数组
int T[maxn];//第i棵线段树的根节点
int a[maxn];//原数组
int H[maxn];//原数组排序之后的数组
int m;//总共不同数的个数,也就是每棵线段树的大小
struct ask{
int l, r;
int k;
}Ask[maxn];//由于要整体先hash,所以必须离线处理
void init()
{
cnt = 0;
sort(H, H + m);
m = unique(H, H + m) - H;
}
int Hash(int x)
{
return lower_bound(H, H + m, x) - H;
}
int lowbit(int x)
{
return x&(-x);
}
int build(int l, int r)//建立一棵空树T[0]
{
int f = cnt++;
Tree[f].sum = 0;
if(l == r) return f;
int mid = (l + r)>>1;
Tree[f].L = build(l, mid);
Tree[f].R = build(mid + 1, r);
return f;
}
int insert(int pa, int x, int value, int l, int r)
{
int now = cnt++;
Tree[now].sum = Tree[pa].sum + value;
if(l == r) return now;
int mid = (l + r)>>1;
if(x <= mid)
{
Tree[now].R = Tree[pa].R;
Tree[now].L = insert(Tree[pa].L, x, value, l, mid);
}
else
{
Tree[now].L = Tree[pa].L;
Tree[now].R = insert(Tree[pa].R, x, value, mid + 1, r);
}
return now;
}
void update(int loc, int x, int value)//树状数组更新
{
for(int i = loc; i <= n; i += lowbit(i))
{
s[i] = insert(s[i], x, value, 0, m - 1);
}
}
int get(int loc, int *term)//树状数组求和
{
int ans = 0;
for(int i = loc; i >= 1; i -= lowbit(i))
{
ans += Tree[Tree[term[i]].L].sum;
}
return ans;
}
int query(int L, int R, int k, int l, int r, int *term1, int *term2, int p1, int p2)
{
if(l == r) return l;
int mid = (l + r)>>1;
int d1 = Tree[Tree[p2].L].sum - Tree[Tree[p1].L].sum;
int x1 = get(R, term1);
int x2 = get(L - 1, term2);
int d2 = x1 - x2;
int d = d1 + d2;
if(k <= d)
{
for(int i = R; i >= 1; i -= lowbit(i))
{
X[i] = Tree[X[i]].L;
}
for(int i = L - 1; i >= 1; i -= lowbit(i))
{
Y[i] = Tree[Y[i]].L;
}
return query(L, R, k, l, mid, X, Y, Tree[p1].L, Tree[p2].L);
}
else
{
for(int i = R; i >= 1; i -= lowbit(i))
{
X[i] = Tree[X[i]].R;
}
for(int i = L - 1; i >= 1; i -= lowbit(i))
{
Y[i] = Tree[Y[i]].R;
}
return query(L, R, k - d, mid + 1, r, X, Y, Tree[p1].R, Tree[p2].R);
}
}
int main()
{
//freopen("C:\\Users\\creator\\Desktop\\in.txt","r",stdin) ;
//freopen("C:\\Users\\creator\\Desktop\\out.txt","w",stdout) ;
int Case;
scanf("%d", &Case);
while(Case--)
{
scanf("%d%d", &n, &q);
m = 0;
for(int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
H[m++] = a[i];
}
char type[10];
for(int i = 1; i <= q; i++)
{
scanf("%s", type);
if(type[0] == 'Q')
{
scanf("%d%d%d", &Ask[i].l, &Ask[i].r, &Ask[i].k);
}
else
{
scanf("%d%d", &Ask[i].l, &Ask[i].r);
Ask[i].k = -1;
H[m++] = Ask[i].r;//后来更新的值也要hash进去
}
}
init();
T[0] = build(0, m - 1);
for(int i = 1; i <= n; i++)
{
T[i] = insert(T[i - 1], Hash(a[i]), 1, 0, m - 1);
}
for(int i = 1; i <= n; i++)
{
s[i] = T[0];
}
for(int i = 1; i <= q; i++)
{
if(Ask[i].k > 0)
{
for(int j = Ask[i].r; j >= 1; j -= lowbit(j))
{
X[j] = s[j];
}
for(int j = Ask[i].l - 1; j >= 1; j -= lowbit(j))
{
Y[j] = s[j];
}
printf("%d\n", H[query(Ask[i].l, Ask[i].r, Ask[i].k, 0, m - 1, X, Y, T[Ask[i].l - 1], T[Ask[i].r])]);
}
else
{
update(Ask[i].l, Hash(a[Ask[i].l]), -1);
update(Ask[i].l, Hash(Ask[i].r), 1);
a[Ask[i].l] = Ask[i].r;
}
}
}
return 0;
}