【bzoj4605】崂山白花蛇草水 权值线段树套KD-tree

题目描述

神犇Aleph在SDOI Round2前立了一个flag:如果进了省队,就现场直播喝崂山白花蛇草水。凭借着神犇Aleph的实力,他轻松地进了山东省省队,现在便是他履行诺言的时候了。蒟蒻Bob特地为他准备了999,999,999,999,999,999瓶崂山白花蛇草水,想要灌神犇Aleph。神犇Aleph求(跪着的)蒟蒻Bob不要灌他,由于神犇Aleph是神犇,蒟蒻Bob最终答应了他的请求,但蒟蒻Bob决定将计就计,也让神犇Aleph回答一些问题。具体说来,蒟蒻Bob会在一个宽敞的广场上放置一些崂山白花蛇草水(可视为二维平面上的一些整点),然后询问神犇Aleph在矩形区域(x1, y1), (x2, y2)(x1≤x2且y1≤y2,包括边界)中,崂山白花蛇草水瓶数第k多的是多少。为了避免麻烦,蒟蒻Bob不会在同一个位置放置两次或两次以上的崂山白花蛇草水,但蒟蒻Bob想为难一下神犇Aleph,希望他能在每次询问时立刻回答出答案。神犇Aleph不屑于做这种问题,所以把这个问题交给了你。

输入

输入的第一行为两个正整数N, Q,表示横纵坐标的范围和蒟蒻Bob的操作次数(包括放置次数和询问次数)。

接下来Q行,每行代表蒟蒻Bob的一个操作,操作格式如下:

首先第一个数字type,表示操作种类。type=1表示放置,type=2表示询问。

若type=1,接下来会有三个正整数x, y, v,表示在坐标整点(x, y)放置v瓶崂山白花蛇草水。(1≤x, y≤N, 1≤v≤10^9)

若type=2,接下来会有五个正整数x1, y1, x2, y2, k,表示询问矩形区域(x1, y1), (x2, y2)中,崂山白花蛇草水瓶数第k多的是多少。

(1≤x1≤x2≤N,1≤y1≤y2≤N,1≤k≤Q)

为了体现程序的在线性,你需要将每次读入的数据(除了type值)都异或lastans,其中lastans表示上次询问的答案。如果上次询问的答案为"NAIVE!ORZzyz."(见样例输出),则将lastans置为0。初始时的lastans为0。

初始时平面上不存在崂山白花蛇草水。

本题共有12组测试数据。对于所有的数据,N≤500,000。

Q的范围见下表:

测试点1-2     Q=1,000

测试点3-7     Q=50,000

测试点8-12     Q=100,000

输出

对于每个询问(type=2的操作),回答崂山白花蛇草水瓶数第k多的是多少。若不存在第k多的瓶数,

请输出"NAIVE!ORZzyz."(输出不含双引号)。

样例输入

10 7
1 1 1 1
1 2 2 3
1 4 1 2
1 3 4 4
2 1 1 4 1 3
2 2 2 3 5 4
2 2 1 4 4 2

样例输出

NAIVE!ORZzyz.
NAIVE!ORZzyz.
3



题解

权值线段树套KD-tree

一开始想到了一个$O(n\sqrt n\log^2n)$的KD-tree套平衡树套二分的傻*做法本以为数据水能卡过去,结果自己做的随机极限数据跑了60s++。。

看来以后是再也不能过于相信数据结构了。

本题正解是权值线段树/替罪羊树套KD-tree,由于蒟蒻没有学替罪羊树就写了动态开点权值线段树。

对于外层权值线段树的每个节点,对应着一棵KD-tree,存储权值在外层节点范围内的所有点。

对于每次询问,如果判定有解,则寻找区间右半部分中点的个数,如果大于等于k则在右区间中找,否则在左区间中找,直至l=r得到答案。

嗯,说起来真是容易。然而这样交上去肯定是必T无疑。

究其原因就是在KD-tree上。(警察叔叔,就是他!)

一开始把“平衡”KD-tree的时间复杂度当成$O(\log n)$的了(这也是我一开始想出那个做法的原因),以为稳过,结果卡得很死。

由于KD-tree不能旋转什么的,所以自然不能保证平衡。

那么我们能做的只有对KD-tree进行重构,但是蒟蒻又不会比例重构(子树大小超过某比例时重构,替罪羊树的操作),所以只能固定点数重构。

然而重构又出了各种各样的问题 = =,改了以后交上去还是TLE。

实在没办法,要了份数据,发现第6、7个点卡不重构的KD-tree,第8、9、10个点数据范围非常大,这导致无论怎么改重构时间都无法通过。

最后只能针对数据来解决问题了(逃),m=50000时是前7个点,m=100000时是后3个点,分情况就好了,最后还是勉强跑过了。

说实话真正考试如果出这种题的话80分真的是满足了。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 3000010
using namespace std;
const int M = 1000000000;
int m , R , ls[N] , rs[N] , root[N] , ts[N] , tot , d , num , x1 , y1 , x2 , y2;
int sta[N] , top;
struct data
{
	int p[2] , mx[2] , mn[2] , si , c[2];
	bool operator<(const data a)const {return p[d] == a.p[d] ? p[d ^ 1] < a.p[d ^ 1] : p[d] < a.p[d];}
}a[N];
bool cmp(int x , int y)
{
	return a[x].p[d] == a[y].p[d] ? a[x].p[d ^ 1] < a[y].p[d ^ 1] : a[x].p[d] < a[y].p[d];
}
void pushup(int k)
{
	int l = a[k].c[0] , r = a[k].c[1];
	a[k].mx[0] = max(a[k].p[0] , max(a[l].mx[0] , a[r].mx[0]));
	a[k].mx[1] = max(a[k].p[1] , max(a[l].mx[1] , a[r].mx[1]));
	a[k].mn[0] = min(a[k].p[0] , min(a[l].mn[0] , a[r].mn[0]));
	a[k].mn[1] = min(a[k].p[1] , min(a[l].mn[1] , a[r].mn[1]));
	a[k].si = a[l].si + a[r].si + 1;
}
void insert(int &k)
{
	if(!k) k = ++num , a[k].p[0] = x1 , a[k].p[1] = y1;
	else if((d == 0 && (x1 == a[k].p[0] ? y1 < a[k].p[1] : x1 < a[k].p[0])) || (d == 1 && (y1 == a[k].p[1] ? x1 < a[k].p[0] : y1 < a[k].p[1]))) d ^= 1 , insert(a[k].c[0]);
	else insert(a[k].c[1]);
	pushup(k);
}
int query(int k)
{
	if(!k || x1 > a[k].mx[0] || y1 > a[k].mx[1] || x2 < a[k].mn[0] || y2 < a[k].mn[1]) return 0;
	if(x1 <= a[k].mn[0] && y1 <= a[k].mn[1] && x2 >= a[k].mx[0] && y2 >= a[k].mx[1]) return a[k].si;
	int ans = (a[k].p[0] >= x1 && a[k].p[1] >= y1 && a[k].p[0] <= x2 && a[k].p[1] <= y2);
	ans += query(a[k].c[0]);
	ans += query(a[k].c[1]);
	return ans;
}
int solve(int k , int l , int r , int x)
{
	if(l == r) return l;
	int mid = (l + r) >> 1 , sum = query(root[rs[x]]);
	if(sum >= k) return solve(k , mid + 1 , r , rs[x]);
	else return solve(k - sum , l , mid , ls[x]);
}
int build(int l , int r , int now)
{
	if(l > r) return 0;
	int mid = (l + r) >> 1 , pos;
	d = now , nth_element(sta + l , sta + mid , sta + r + 1 , cmp) , pos = sta[mid];
	a[pos].c[0] = build(l , mid - 1 , now ^ 1);
	a[pos].c[1] = build(mid + 1 , r , now ^ 1);
	pushup(pos);
	return pos;
}
void dfs(int k)
{
	if(!k) return;
	sta[++top] = k;
	dfs(a[k].c[0]) , dfs(a[k].c[1]);
}
void rebuild(int &k)
{
	top = 0 , dfs(k);
	k = build(1 , top , 0);
}
void add(int p , int l , int r , int &x)
{
	if(!x) x = ++tot;
	d = 0 , insert(root[x]) , ts[x] ++ ;
	if(m == 50000 && ts[x] >= 2000) rebuild(root[x]) , ts[x] = 0;
	if(l == r) return;
	int mid = (l + r) >> 1;
	if(p <= mid) add(p , l , mid , ls[x]);
	else add(p , mid + 1 , r , rs[x]);
}
int main()
{
	a[0].mx[0] = a[0].mx[1] = -M , a[0].mn[0] = a[0].mn[1] = M;
	int ans = 0 , opt , v , i;
	scanf("%*d%d" , &m);
	for(i = 1 ; i <= m ; i ++ )
	{
		scanf("%d" , &opt);
		if(opt == 1)
		{
			scanf("%d%d%d" , &x1 , &y1 , &v);
			x1 ^= ans , y1 ^= ans , v ^= ans;
			add(v , 1 , M , R);
		}
		else
		{
			scanf("%d%d%d%d%d" , &x1 , &y1 , &x2 , &y2 , &v);
			x1 ^= ans , y1 ^= ans , x2 ^= ans , y2 ^= ans , v ^= ans;
			if(query(root[R]) < v) puts("NAIVE!ORZzyz.") , ans = 0;
			else printf("%d\n" , ans = solve(v , 1 , M , R));
		}
	}
	return 0;
}
时间: 2024-08-04 19:33:05

【bzoj4605】崂山白花蛇草水 权值线段树套KD-tree的相关文章

【BZOJ4605】崂山白花蛇草水 权值线段树+kd-tree

[BZOJ4605]崂山白花蛇草水 Description 神犇Aleph在SDOI Round2前立了一个flag:如果进了省队,就现场直播喝崂山白花蛇草水.凭借着神犇Aleph的实力,他轻松地进了山东省省队,现在便是他履行诺言的时候了.蒟蒻Bob特地为他准备了999,999,999,999,999,999瓶崂山白花蛇草水,想要灌神犇Aleph.神犇Aleph求(跪着的)蒟蒻Bob不要灌他,由于神犇Aleph是神犇,蒟蒻Bob最终答应了他的请求,但蒟蒻Bob决定将计就计,也让神犇Aleph回答

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

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

【BZOJ3110】【codevs1616】K大数查询,权值线段树套普通线段树

Time:2016.05.09 Author:xiaoyimi 转载注明出处谢谢 传送门1 传送门2 思路: 之前没怎么接触过权值线段树(非主席树),这次就当学习了一下吧.一开始还把题意理解错了,我的天啊-- 起初思考了好久,发现不知道怎么处理负数的情况,不过数据里并没有负数? 权值线段树的每个节点表示一个区间[L,R],存储原序列中权值为[L,R]的元素的信息,所以这里的权值线段树每个节点上都是一棵普通线段树,就是负责统计原序列中权值为[L,R]的元素的个数. 每次插入一个相同的值x时就相当于

BZOJ 3110 ZJOI 2013 K大数查询 树套树(权值线段树套区间线段树)

题目大意:有一些位置,这些位置上可以放若干个数字.现在有两种操作. 1.在区间l到r上添加一个数字x 2.求出l到r上的第k大的数字是什么 思路:这种题一看就是树套树,关键是怎么套,怎么写.(话说我也不会来着..)最容易想到的方法就是区间线段树套一个权值线段树,但是区间线段树上的标记就会变得异常复杂.所以我们就反过来套,用权值线段树套区间线段树.这样修改操作在外线段树上就变成了单点修改,外线段树就不用维护标记了.在里面的区间线段树上维护标记就容易多了.具体实现见代码. CODE: #includ

[bzoj3932][CQOI2015]任务查询系统-题解[主席树][权值线段树]

Description 最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分.超级计算机中的 任务用三元组(Si,Ei,Pi)描述,(Si,Ei,Pi)表示任务从第Si秒开始,在第Ei秒后结束(第Si秒和Ei秒任务也在运行 ),其优先级为Pi.同一时间可能有多个任务同时执行,它们的优先级可能相同,也可能不同.调度系统会经常向 查询系统询问,第Xi秒正在运行的任务中,优先级最小的Ki个任务(即将任务按照优先级从小到大排序后取前Ki个 )的优先级之和是多少.特别的,如

P2234 [HNOI2002]营业额统计 (权值线段树)

P2234 [HNOI2002]营业额统计 题目描述 Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况. Tiger拿出了公司的账本,账本上记录了公司成立以来每天的营业额.分析营业情况是一项相当复杂的工作.由于节假日,大减价或者是其他情况的时候,营业额会出现一定的波动,当然一定的波动是能够接受的,但是在某些时候营业额突变得很高或是很低,这就证明公司此时的经营状况出现了问题.经济管理学上定义了一种最小波动值来衡量这种情况: 当最小波动值越大

BZOJ4605 : 崂山白花蛇草水

外层维护权值线段树,内层维护kd-tree. 修改的时候只往右儿子里插入,不平衡的时候替罪羊式重构. 查询的时候在外层线段树上走,在内层kd-tree上查询矩形内点数即可. 时间复杂度$O(q\log v(\log^2q+\sqrt{q}))$. #include<cstdio> #include<algorithm> #include<cmath> using namespace std; const double A=0.8; const int N=50010,M

【bzoj2161】布娃娃 权值线段树

题目描述 小时候的雨荨非常听话,是父母眼中的好孩子.在学校是老师的左右手,同学的好榜样.后来她成为艾利斯顿第二代考神,这和小时候培养的良好素质是分不开的.雨荨的妈妈也为有这么一个懂事的女儿感到高兴.一次期末考试,雨荨不知道第多少次,再次考了全年级第一名.雨荨的妈妈看到女儿100分的成绩单时,脸上又泛起了幸福的笑容,作为奖励,她给雨荨买了n个布娃娃.细心的雨荨发现,第i个布娃娃有一个耐心值P[i]以及一个魅力值C[i],并且还有能够忍受的耐心值的上限R[i]以及下限L[i].当一个布娃娃j满足L[

模板——权值线段树(逆序对)

Ultra-QuickSort Time Limit: 7000MS   Memory Limit: 65536K Total Submissions: 62455   Accepted: 23259 Description In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a sequence of n distinct integers by swappin