bzoj1901树套树的一种解法

Description

给定一个含有n个数的序列a[1],a[2],a[3]……a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[i+2]……a[j]中第k小的数是多少(1≤k≤j-i+1),并且,你可以改变一些a[i]的值,改变后,程序还能针对改变后的a继续回答上面的问题。你需要编一个这样的程序,从输入文件中读入序列a,然后读入一系列的指令,包括询问指令和修改指令。对于每一个询问指令,你必须输出正确的回答。 第一行有两个正整数n(1≤n≤10000),m(1≤m≤10000)。分别表示序列的长度和指令的个数。第二行有n个数,表示a[1],a[2]……a[n],这些数都小于10^9。接下来的m行描述每条指令,每行的格式是下面两种格式中的一种。 Q i j k 或者 C i t Q i j k (i,j,k是数字,1≤i≤j≤n, 1≤k≤j-i+1)表示询问指令,询问a[i],a[i+1]……a[j]中第k小的数。C i t (1≤i≤n,0≤t≤10^9)表示把a[i]改变成为t。

Input

对于每一次询问,你都需要输出他的答案,每一个输出占单独的一行。

Output

Sample Input

5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3

Sample Output

3
6

HINT

20%的数据中,m,n≤100; 40%的数据中,m,n≤1000; 100%的数据中,m,n≤10000。

        此题解法很多,可以用整体二分(我不会),可以用主席树(我忘记了),恰好这段时间我刚想明白了树套树,然后我心血来潮写出了这道我认为是神犇才能AC的题(然而写出来了才发现我还有很长的一条路要走)。
         好了回归正题,首先我们考虑树套树,我想到的是线段树套权值线段树(当然树状数组更快,原理一样不再累述)。
        第一层我们按序列建树,然后每个节点套个权值线段树,SUM表示的是这个区间有几个数出现过,删除的时候SUM[K]--,插入 的时候SUM[K]++,一次修改操作可以分为一个删除和一个插入(很傻比是吧),然后查区间K大的时候,我一开始想成只要在区间内,就立即查K大,如果K>sum[root[k]]就拿K-SUN[ROOT[K]],我竟然逗比地认为线段树左边的数<右边的树,然后我花了1H查错(果然我是猪),意识到后发现需要把在L到R之内的权值线段树拿去合并,我的做法是记一个数组B保存ROOT,然后这课权值线段树的SUM[LC[K]]即为sigmaSUM[LC[B[I]]],然后判断一下如果K(这里指第K大那个K)如果小于等于SUM[LC[K]]就走左边,否则走右边,然后拿K减去SUM[LC[K]]。走左边的话就把全部的B[]赋值成LC[B[]],走右边也是一个意思,然后拿两个P,Q,搞搞,最后P==Q时P就是答案。(这题重复的数很坑不过按我这么处理是不会有问题的)

        附上我的代码 

#include
#include
#include
#include
using namespace std;
const int INF=1000000000;
const int MAXX=10010;
char s[5];
int n,m,i,j,k,x,y,z,a[MAXX],b[101],lenb,len;
int sum[MAXX*360],lc[MAXX*360],rc[MAXX*360],root[MAXX*4];
int get()
{
    char c;
    while (((c=getchar())<48||c>57)&&c!='-');
    if (c=='-')
    {
        int res=0;
        while ((c=getchar())>=48&&c<=57)
        res=res*10+c-'0';
        return -res;
    }
    else{
        int res=c-'0';
        while ((c=getchar())>=48&&c<=57)
        res=res*10+c-'0';
        return res;
    }
}
void insert1(int &k,int p,int q,int w)
{
	if (!k) k=++len;
	if (p==q&&p==w)
	{
		sum[k]++;
		return;
	}
	int mid=(p+q)>>1;
	if (mid>=w) insert1(lc[k],p,mid,w);
	if (mid>1;
	if (mid>=w) delet1(lc[k],p,mid,w);
	if (mid>1;
	if (mid>=t) delet(k<<1,p,mid,t,w);
	if (mid>1;
	if (mid>=t) insert(k<<1,p,mid,t,w);
	if (mid>1,tot=0;
		for(int i=1;i<=lenb;i++)
		tot+=sum[lc[b[i]]];
		if (k<=tot)
		{
			q=mid;
			for(int i=1;i<=lenb;i++)
			b[i]=lc[b[i]];
		}
		else 
		{
			p=mid+1;
			for(int i=1;i<=lenb;i++)
			b[i]=rc[b[i]];
			k-=tot;
		}
	}
	return p;
}
void find(int k,int p,int q,int l,int r)
{
	if (p>=l&&q<=r)
	{
		b[++lenb]=root[k];
		return;
	}
	int mid=(p+q)>>1;
	if (mid>=l) find(k<<1,p,mid,l,r);
	if (mid




你可能感兴趣的:(树套树)