[树套树]K大数查询

有N个位置,M个操作。操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c
如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少。
为了强制在线,每一次的a,b是加密的,需要异或lastans的后8位进行解密,其中lastans为上次输出的结果,初始为零。
如果解密后a>b则先交换a,b
数据保证解密后a,b不会超过N
如果解密后a,b出现0,则赋值为1。

来历:bzoj上的一道题,经过子祯学长的魔改后,数据范围变得很奇怪...

算法:树套树;

权值线段树套线段树;

套线段树有技巧:一颗线段树可以先只创造一个点,其他需要的节点需要用到的时候再开就可以了;

其他的也没什么可说了,看代码吧:

(不要交到bzoj上,过不了,这是经过魔改的题目)

(不建议借鉴,初次写这种题,code还比较稚嫩)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<algorithm>
#include<iomanip>
#include<stack>
using namespace std;
#define FILE "dealing"
#define up(i,j,n) for(int i=(j);i<=(n);i++)
#define pii pair<int,int>
#define LL long long
#define mem(f,g) memset(f,g,sizeof(f))
namespace IO{
	char buf[1<<15],*fs,*ft;
	int gc(){return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?-1:*fs++;}
	int read(){
		int ch=gc(),f=0,x=0;
		while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=1;ch=gc();}
		while(ch>=‘0‘&&ch<=‘9‘){x=(x<<1)+(x<<3)+ch-‘0‘;ch=gc();}
		return f?-x:x;
	}
	int readint(){
		int ch=getchar(),f=0,x=0;
		while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=1;ch=getchar();}
		while(ch>=‘0‘&&ch<=‘9‘){x=(x<<1)+(x<<3)+ch-‘0‘;ch=getchar();}
		return f?-x:x;
	}
}using namespace IO;
const int maxn=201000,inf=1000000000;
int n,m;
int ch[maxn],l[maxn],r[maxn],k[maxn],f[maxn];
struct node{
	int k,id;
	bool operator<(const node& b)const{return k<b.k;}
}Cnt[maxn];
int L=0;
namespace OI{
	#define mid ((l+r)>>1)
	const int maxm=20000000;
	int root=0,c[maxn][2],id[maxn],cnt=0;//权值线段树
	LL sum[maxm],flag[maxm];
	int d[maxm][2],len=0;//区间线段树
	int x,y,key;
	void pushdown(int o,int l,int r){
		if(flag[o]){
			if(l!=r&&!d[o][0])d[o][0]=++len;
			if(l!=r&&!d[o][1])d[o][1]=++len;
			sum[d[o][0]]+=flag[o]*(mid-l+1);
			sum[d[o][1]]+=flag[o]*(r-mid);
			flag[d[o][0]]+=flag[o];
			flag[d[o][1]]+=flag[o];
			flag[o]=0;
		}
	}
	void change(int l,int r,int& o){
		if(l>y||r<x)return;
		if(!o)o=++len;
		if(l>=x&&r<=y){
			sum[o]+=r-l+1;
			flag[o]++;
			return;
		}
		pushdown(o,l,r);
		change(l,mid,d[o][0]);
		change(mid+1,r,d[o][1]);
		sum[o]=sum[d[o][1]]+sum[d[o][0]];
	}
	void insert(int l,int r,int& o){
		if(l>key||r<key)return;
		if(!o)o=++cnt;
		change(1,n,id[o]);
		if(l==key&&r==key)return;
		insert(l,mid,c[o][0]);
		insert(mid+1,r,c[o][1]);
	}
	void init(int l,int r,int k){
		x=l,y=r,key=k;
		insert(1,L,root);
	}
	int query2(int l,int r,int o){
		if(l>y||r<x||!o)return 0;
		if(l>=x&&r<=y)return sum[o];
		pushdown(o,l,r);
		return query2(l,mid,d[o][0])+query2(mid+1,r,d[o][1]);
	}
	int query1(int l,int r,int o){
		if(!o||r<=key)return 0;
		if(l>key)return query2(1,n,id[o]);
		return query1(l,mid,c[o][0])+query1(mid+1,r,c[o][1]);
	}
	int Query(int l,int r,int val){
		x=l,y=r,key=val;
		return query1(1,L,root)+1;
	}
	int Q(int l,int r,int k){
		int left=1,right=L,midd,p;
		while(left+1<right){
			midd=(left+right)>>1;
			if(Query(l,r,midd)>k)left=midd;
			else right=midd;
		}
		if(Query(l,r,left)>k&&Query(l,r,right)<=k)return right;
		else return left;
	}
}using namespace OI;
void print(int *a,int n){up(i,1,n)printf("%d ",a[i]);cout<<endl;}
int main(){
	freopen(FILE".in","r",stdin);
	freopen(FILE".out","w",stdout);
	n=read(),m=read();
	up(i,1,m){
		ch[i]=read(),l[i]=read(),r[i]=read(),k[i]=read();
		if(ch[i]==1)Cnt[++L].k=k[i],Cnt[L].id=i;
	}
	sort(Cnt+1,Cnt+L+1);
	up(i,1,L)f[Cnt[i].id]=i;
	int pre=0,d=(1LL<<8)-1;
	up(i,1,m){
		l[i]^=(pre&d),r[i]^=(pre&d);
		if(!l[i])l[i]=1;if(!r[i])r[i]=1;
		if(l[i]>r[i])swap(l[i],r[i]);
		if(ch[i]==1)init(l[i],r[i],f[i]);
		if(ch[i]==2)printf("%d\n",pre=Cnt[Q(l[i],r[i],k[i])].k);
	}
	return 0;
}

  

时间: 2024-10-31 04:03:55

[树套树]K大数查询的相关文章

[CDQ分治][树状数组][树套树] Jzoj P3197 K大数查询

Description 有n 个位置和m 个操作.操作有两种,每次操作如果是1 a b c 的形式,表示往第a 个位置到第b 个位置每个位置加入一个数c.如果操作形如2 a b c 的形式,表示询问从第a 个位置到第b 个位置,第c 大的数是多少. Input 在输入文件sequence.in 中,第一行两个数n,m.意义如题目描述.接下来m 行每行形如1 a b c 或者2 a b c 如题目描述. Output 在输出文件sequence.out 中,对于每个询问回答k 大数是多少. Sam

BZOJ 3110: [Zjoi2013]K大数查询 [树套树]

3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 6050  Solved: 2007[Submit][Status][Discuss] Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M接下来M行,每行形如1 a

3110: [Zjoi2013]K大数查询 树状数组套线段树

3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1384  Solved: 629[Submit][Status] Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M接下来M行,每行形如1 a b c或2 a b

BZOJ 3110 K大数查询 树套树

题意:链接 方法:树套树(线段树套线段树) 题解:这题好神啊- -自己在做的时候一顿yy也没yy出用两个线段树来搞,其实我想的是类似二逼那道题那样,用线段树维护总区间,treap维护每个节点,不过这样的话,尼玛修改就是暴力有没有?而且查询的时候也是暴力啊有没有?绝壁不是这么做的啊! 上网上找了找题解综合了大家的思想自己也是懂了这题是咋回事了,也是跪了. 好不扯淡了,谈正经的,两个线段树是怎么搞得. 其实第一棵线段树是区间的线段树,而第二棵是维护值域的线段树,具体解析请看代码. #include

树套树专题——bzoj 3110: [Zjoi2013] K大数查询 &amp; 3236 [Ahoi2013] 作业 题解

[原题1] 3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MB Submit: 978  Solved: 476 Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c 如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M 接下来M行,每行形如1 a b c或2 a b c Outpu

BZOJ 3110: [Zjoi2013]K大数查询( 树状数组套主席树 )

BIT+(可持久化)权值线段树, 用到了BIT的差分技巧. 时间复杂度O(Nlog^2(N)) ----------------------------------------------------------------------------------------- #include<cstdio> #include<cctype> #include<cstring> #include<algorithm> using namespace std;

【BZOJ3110】[Zjoi2013]K大数查询 树套树

[BZOJ3110][Zjoi2013]K大数查询 Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c,如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M接下来M行,每行形如1 a b c或2 a b c Output 输出每个询问的结果 Sample Input 2 5 1 1 2 1 1 1 2 2 2 1 1 2 2 1 1 1 2 1 2 3

数据结构(树套树):ZJOI 2013 K大数查询

有几个点卡常数…… 发现若第一维为位置,第二维为大小,那么修改时第一维修改区间,查询时第一维查询区间,必须挂标记.而这种情况下标记很抽象,而且Push_down不是O(1)的,并不可行. 那要怎么做呢?不妨交换一下,第一维为大小,第二维为位置,在第二维中挂标记,这样Push_down就是O(1)的了. 做完这道题,我最大的启发就是:树套树不适于在第一维挂标记,因为标记的维度会是一维的,根本不好维护. 1 #include <iostream> 2 #include <cstring>

【BZOJ3110】【Zjoi2013】K大数查询 树套树 权值线段树套区间线段树

#include <stdio.h> int main() { puts("转载请注明出处谢谢"); puts("http://blog.csdn.net/vmurder/article/details/43020009"); } 题解: 外层权值线段树,内层区间线段树可解. 权值都是1~n,就不用离散化了. 我写了标记永久化. 其它心得神马的: 天生对树形数据结构无爱. 第一次写树套树,终于知道是怎么回事了. (只针对本题) 就是外层每个点都表示了一段