【bzoj4605】崂山白花蛇草水 权值线段树套KD-tree
题目描述
输入
输出
样例输入
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
对于外层权值线段树的每个节点,对应着一棵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;
}
17.12.18 UPD
写了一下替罪羊式重构的写法,好像也不是很难嘛。。。
在KD-tree插入过程中直接判断一个点是否失去平衡,是的话就直接重构即可。方法和替罪羊树类似,可以参考替罪羊树例题 【bzoj3065】带插入区间k小值 。
并且稍微优化了一下代码的细节,现在差不多可读了。。。
时间复杂度终于是稳定的 $O(n\sqrt n\log n)$ 了,终于不用针对数据啦。
#include <cstdio>
#include <algorithm>
#define N 100010
#define M 3000010
using namespace std;
struct seg
{
int ls , rs , rb;
}a[M];
struct kdt
{
int p[2] , mx[2] , mn[2] , ls , rs , si;
}b[M];
int A[2] , B[2] , d , root , id[N] , ta , tb , tot;
inline bool tc(int *a , int *b)
{
return a[d] == b[d] ? a[d ^ 1] < b[d ^ 1] : a[d] < b[d];
}
bool cmp(int x , int y)
{
return tc(b[x].p , b[y].p);
}
inline void pushup(int x)
{
int l = b[x].ls , r = b[x].rs;
b[x].mx[0] = max(b[x].p[0] , max(b[l].mx[0] , b[r].mx[0]));
b[x].mx[1] = max(b[x].p[1] , max(b[l].mx[1] , b[r].mx[1]));
b[x].mn[0] = min(b[x].p[0] , min(b[l].mn[0] , b[r].mn[0]));
b[x].mn[1] = min(b[x].p[1] , min(b[l].mn[1] , b[r].mn[1]));
b[x].si = b[l].si + b[r].si + 1;
}
int build(int l , int r , int now)
{
if(l > r) return 0;
int mid = (l + r) >> 1;
d = now , nth_element(id + l , id + mid , id + r + 1 , cmp);
b[id[mid]].ls = build(l , mid - 1 , now ^ 1);
b[id[mid]].rs = build(mid + 1 , r , now ^ 1);
pushup(id[mid]);
return id[mid];
}
void dfs(int k)
{
if(k) dfs(b[k].ls) , id[++tot] = k , dfs(b[k].rs);
}
void insert(int &k , int now , int flag)
{
if(!k)
{
k = ++tb , b[k].p[0] = A[0] , b[k].p[1] = A[1] , pushup(k);
return;
}
bool tag;
d = now;
if(tc(A , b[k].p)) tag = ((b[b[k].ls].si + 1) * 4 > (b[k].si + 1) * 3) , insert(b[k].ls , now ^ 1 , tag | flag);
else tag = ((b[b[k].rs].si + 1) * 4 > (b[k].si + 1) * 3) , insert(b[k].rs , now ^ 1 , tag | flag);
pushup(k);
if(tag && !flag) tot = 0 , dfs(k) , k = build(1 , tot , now);
}
int query(int k)
{
if(!k || b[k].mx[0] < A[0] || b[k].mx[1] < A[1] || b[k].mn[0] > B[0] || b[k].mn[1] > B[1]) return 0;
if(b[k].mn[0] >= A[0] && b[k].mn[1] >= A[1] && b[k].mx[0] <= B[0] && b[k].mx[1] <= B[1]) return b[k].si;
return (b[k].p[0] >= A[0] && b[k].p[1] >= A[1] && b[k].p[0] <= B[0] && b[k].p[1] <= B[1]) + query(b[k].ls) + query(b[k].rs);
}
void modify(int p , int l , int r , int &x)
{
if(!x) x = ++ta;
insert(a[x].rb , 0 , 0);
if(l == r) return;
int mid = (l + r) >> 1;
if(p <= mid) modify(p , l , mid , a[x].ls);
else modify(p , mid + 1 , r , a[x].rs);
}
int solve(int k , int l , int r , int x)
{
if(l == r) return l;
int mid = (l + r) >> 1 , t = query(a[a[x].rs].rb);
if(k <= t) return solve(k , mid + 1 , r , a[x].rs);
else return solve(k - t , l , mid , a[x].ls);
}
int main()
{
b[0].mx[0] = b[0].mx[1] = -1 << 30 , b[0].mn[0] = b[0].mn[1] = 1 << 30;
int m , opt , x , last = 0;
scanf("%*d%d" , &m);
while(m -- )
{
scanf("%d" , &opt);
if(opt == 1) scanf("%d%d%d" , &A[0] , &A[1] , &x) , A[0] ^= last , A[1] ^= last , x ^= last , modify(x , 0 , 1000000000 , root);
else
{
scanf("%d%d%d%d%d" , &A[0] , &A[1] , &B[0] , &B[1] , &x), A[0] ^= last , A[1] ^= last , B[0] ^= last , B[1] ^= last , x ^= last;
if(!(last = solve(x , 0 , 1000000000 , root))) puts("NAIVE!ORZzyz.");
else printf("%d\n" , last);
}
}
return 0;
}
【bzoj4605】崂山白花蛇草水 权值线段树套KD-tree的更多相关文章
- bzoj4605: 崂山白花蛇草水 权值线段树套KDtree
bzoj4605: 崂山白花蛇草水 链接 bzoj loj 思路 强制在线,那就权值线段树套KDtree好了,没啥好讲的. KDtree要加平衡因子来重构.另外,那水真难喝. 错误 树套树一边写过了, ...
- 崂山白花蛇草水 权值线段树套KDtree
Description 神犇Aleph在SDOI Round2前立了一个flag:如果进了省队,就现场直播喝崂山白花蛇草水.凭借着神犇Aleph的实 力,他轻松地进了山东省省队,现在便是他履行诺言的时 ...
- 【BZOJ4605】崂山白花蛇草水 权值线段树+kd-tree
[BZOJ4605]崂山白花蛇草水 Description 神犇Aleph在SDOI Round2前立了一个flag:如果进了省队,就现场直播喝崂山白花蛇草水.凭借着神犇Aleph的实力,他轻松地进了 ...
- 洛谷P4848 崂山白花蛇草水 权值线段树+KDtree
题目描述 神犇 \(Aleph\) 在 \(SDOI\ Round2\) 前立了一个 \(flag\):如果进了省队,就现场直播喝崂山白花蛇草水.凭借着神犇 \(Aleph\) 的实力,他轻松地进了山 ...
- [bzoj3196][Tyvj1730]二逼平衡树_树套树_位置线段树套非旋转Treap/树状数组套主席树/权值线段树套位置线段树
二逼平衡树 bzoj-3196 Tyvj-1730 题目大意:请写出一个维护序列的数据结构支持:查询给定权值排名:查询区间k小值:单点修改:查询区间内定值前驱:查询区间内定值后继. 注释:$1\le ...
- [BZOJ 3110] [luogu 3332] [ZJOI 2013]k大数查询(权值线段树套线段树)
[BZOJ 3110] [luogu 3332] [ZJOI 2013]k大数查询(权值线段树套线段树) 题面 原题面有点歧义,不过从样例可以看出来真正的意思 有n个位置,每个位置可以看做一个集合. ...
- BZOJ 3110 ZJOI 2013 K大数查询 树套树(权值线段树套区间线段树)
题目大意:有一些位置.这些位置上能够放若干个数字. 如今有两种操作. 1.在区间l到r上加入一个数字x 2.求出l到r上的第k大的数字是什么 思路:这样的题一看就是树套树,关键是怎么套,怎么写.(话说 ...
- BZOJ3110[Zjoi2013]K大数查询——权值线段树套线段树
题目描述 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是 ...
- 【bzoj3110】[Zjoi2013]K大数查询 权值线段树套区间线段树
题目描述 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c.如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数 ...
随机推荐
- 进程间通信,把字符串指针作为参数通过SendMessage传递给另一个进程,不起作用
参数发送进程: CString csCmd=AfxGetApp()->m_lpCmdLine; if (!csCmd.IsEmpty()) { pWndPrev->SendMessage( ...
- 深入浅出Android动态加载jar包技术
在实际项目中,由于某些业务频繁变更而导致频繁升级客户端的弊病会造成较差的用户体验,而这也恰是Web App的优势,于是便衍生了一种思路,将核心的易于变更的业务封装在jar包里然后通过网络下载下来,再由 ...
- fiddler+willow问题总结
本文纯属用来记录自己学习过程中遇到的坑,如有朋友也遇到,可移步到这里查看是否为该问题导致. fiddler 安装不用说了,到官网直接去下载,自行下载最新版本 willow下载地址:http://qzo ...
- Mysql常用的优化技巧
1.通过show status 命令了解各种sql的执行效率 2. 定位执行效率较低的SQL语句 开启慢查询记录: 打开Mysql配置文件my.ini ,找到 [mysqld] 在其下面添加 long ...
- spring 上传附件
jsp: <form class='uk-form' action="savelead" method="post" enctype="mult ...
- sqlserver数据库备份方法
须事先准备一个工具curl,把它放在c盘.然后,在数据库所在服务器安装7z.最后把这2个存储过程执行,在sqlserver的代理中新建作业,即可实现备份操作. --备份指定数据库到本地和远程指定位置( ...
- 2017四川省赛E题( Longest Increasing Subsequence)
提交地址: https://www.icpc-camp.org/contests/4rgOTH2MbOau7Z 题意: 给出一个整数数组,F[i]定义为以i结尾的最长上升子序列,然后问以此删除掉第i个 ...
- 广播接收者 BroadcastReceiver
1. 分为动态注册和静态注册, 静态注册在清单文件里配置即可.动态创建为代码手动添加. 在锁屏广播中, 使用静态创建消息接受不成功, 原因未知. 动态即可. 代码如下: 2. 创建类, 继承与Broa ...
- 并查集+思维——The Door Problem
一.问题描述(题目链接) 有n个门和m个开关,每个开关可以控制任意多的门,每个门严格的只有两个开关控制,问能否通过操作某些开关使得所有门都打开.(给出门的初始状态). 二.问题分析 大部分开关问题首先 ...
- 在Terminal中,如何打开Finder,并显示当前的目录
这是一个非常方便实用的小技巧,在Terminal中输入如下命令: $ open . 有图有真相: 参考: Open Finder in Current Folder from Terminal