bzoj1901树套树的一种解法

<h2 style="font-family: arial, verdana, helvetica, sans-serif; color: blue;">Description</h2><div class="content" style="font-family: 'Times New Roman'; height: auto; background-color: rgb(228, 240, 248); margin: 0px; padding: 0px 20px; font-size: 18px !important;"><p style="font-family: arial, verdana, helvetica, sans-serif;">给定一个含有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。</p></div><h2 style="font-family: arial, verdana, helvetica, sans-serif; color: blue;">Input</h2><div class="content" style="font-family: 'Times New Roman'; height: auto; background-color: rgb(228, 240, 248); margin: 0px; padding: 0px 20px; font-size: 18px !important;"><p style="font-family: arial, verdana, helvetica, sans-serif;">对于每一次询问,你都需要输出他的答案,每一个输出占单独的一行。</p></div><h2 style="font-family: arial, verdana, helvetica, sans-serif; color: blue;">Output</h2><div class="content" style="font-family: 'Times New Roman'; height: auto; background-color: rgb(228, 240, 248); margin: 0px; padding: 0px 20px; font-size: 18px !important;"></div><h2 style="font-family: arial, verdana, helvetica, sans-serif; color: blue;">Sample Input</h2><div class="content" style="font-family: 'Times New Roman'; height: auto; background-color: rgb(228, 240, 248); margin: 0px; padding: 0px 20px; font-size: 18px !important;"><span class="sampledata" style="font-family: monospace; white-space: pre; background-color: rgb(141, 184, 255); font-size: 18px; background-position: initial initial; background-repeat: initial initial;">5 3<br style="font-family: arial, verdana, helvetica, sans-serif;" />3 2 1 4 7<br style="font-family: arial, verdana, helvetica, sans-serif;" />Q 1 4 3<br style="font-family: arial, verdana, helvetica, sans-serif;" />C 2 6<br style="font-family: arial, verdana, helvetica, sans-serif;" />Q 2 5 3<br style="font-family: arial, verdana, helvetica, sans-serif;" /></span></div><h2 style="font-family: arial, verdana, helvetica, sans-serif; color: blue;">Sample Output</h2><div class="content" style="font-family: 'Times New Roman'; height: auto; background-color: rgb(228, 240, 248); margin: 0px; padding: 0px 20px; font-size: 18px !important;"><span class="sampledata" style="font-family: monospace; white-space: pre; background-color: rgb(141, 184, 255); font-size: 18px; background-position: initial initial; background-repeat: initial initial;">3<br style="font-family: arial, verdana, helvetica, sans-serif;" />6<br style="font-family: arial, verdana, helvetica, sans-serif;" /></span></div><h2 style="font-family: arial, verdana, helvetica, sans-serif; color: blue;">HINT</h2><div class="content" style="font-family: 'Times New Roman'; height: auto; background-color: rgb(228, 240, 248); margin: 0px; padding: 0px 20px; font-size: 18px !important;"><p style="font-family: arial, verdana, helvetica, sans-serif;"></p><p style="font-family: arial, verdana, helvetica, sans-serif;">20%的数据中,m,n≤100; 40%的数据中,m,n≤1000; 100%的数据中,m,n≤10000。</p></div>
        此题解法很多,可以用整体二分(我不会),可以用主席树(我忘记了),恰好这段时间我刚想明白了树套树,然后我心血来潮写出了这道我认为是神犇才能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<algorithm>
#include<iostream>
#include<cstdio>
#include<cmath>
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<w) insert1(rc[k],mid+1,q,w);
	sum[k]=sum[lc[k]]+sum[rc[k]];
}
void delet1(int k,int p,int q,int w)
{
	if (p==q&&p==w)
	{
		if (sum[k]) sum[k]--;
		return;
	}
	int mid=(p+q)>>1;
	if (mid>=w) delet1(lc[k],p,mid,w);
	if (mid<w) delet1(rc[k],mid+1,q,w);
	sum[k]=sum[lc[k]]+sum[rc[k]];
}
void delet(int k,int p,int q,int t,int w)
{
	delet1(root[k],0,INF,w);
	if (p==t&&p==q) return;
	int mid=(p+q)>>1;
	if (mid>=t) delet(k<<1,p,mid,t,w);
	if (mid<t) delet((k<<1)+1,mid+1,q,t,w);
}
void insert(int k,int p,int q,int t,int w)
{
	insert1(root[k],0,INF,w);
	if (p==t&&p==q) return;
	int mid=(p+q)>>1;
	if (mid>=t) insert(k<<1,p,mid,t,w);
	if (mid<t) insert((k<<1)+1,mid+1,q,t,w);
}
int SEARCH(int p,int q,int k)
{
	while (p!=q)
	{
		int mid=(p+q)>>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<r) find((k<<1)+1,mid+1,q,l,r);
}
int main()
{
	n=get();m=get();
	for(i=1;i<=n;i++)
	{
		x=get();
		a[i]=x;
		insert(1,1,n,i,x);
	}
	for(i=1;i<=m;i++)
	{
		scanf("%s",s);
		if (s[0]=='C')
		{
			x=get();y=get();
			delet(1,1,n,x,a[x]);
			insert(1,1,n,x,y);
			a[x]=y;
		}
		else
		{
			x=get();y=get();k=get();
			lenb=0;
			find(1,1,n,x,y);
			printf("%d\n",SEARCH(0,INF,k));
		}
	}
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值