[JZOJ2865]【集训队互测 2012】Attack
题目
题目大意
平面上有一堆带权值的点。两种操作:交换两个点的权值,查找一个矩形的第\(k\)小
\(N<=60000\)
\(M<=10000\)
\(10000ms\)
思考历程&各种可能过的方法
先是想了一会儿,然后突然发现一个惊天大秘密:\(10000ms\)!
然后就想出个\(O(NM)\)的做法……
将矩形内的所有点找出来,然后\(O(N)\)求第\(k\)大……
于是爆\(0\)了。后来才发现是输出的时候漏了句号,而且给出的矩形有\(x0>x1\)或\(y0>y1\)的情况……
改过来,\(50+\)
其实\(O(N)\)求第\(k\)大带巨大常数,不如优化:
首先将所有的点按照权值从小到大排序,然后扫过去就可以了。遇见第\(k\)个在矩形内的,直接退出。
然后就有了\(100\)分的好成绩。
当然,这是水法……
考虑一些看着像正解的东西。
首先想到的是树套树套树,显然不现实。
然后就想\(kd-tree\)。二分答案,如果把权值看成一个维,就相当于在一个三维空间中找一个长方体内点的个数。
修改的时候并不需要打个动态的\(kd-tree\)(不然常数大得要死)。我们先将所有修改后形成的新点加进来。每个点上有个标记表示它是否存在。修改的时候修改标记,同时维护一下就可以了。
时间复杂度是\(O(m(n+m)^{\frac{2}{3}}\lg 10^9)\)(如果将权值离散化,\(\lg 10^9\)可以变成\(\lg n\))
感觉上可能不过……
然后又有个方法:\(kd-tree\)套权值线段树!
这样有个好处就是不需要再外面二分答案。
询问的时候在\(kd-tree\)里找对应的节点。找出来的是一堆整块的子树和零散的点。
然后就可以一起二分(也就是每棵权值线段树上的指针一起移动),对于零散的点暴力判就可以了。
时间复杂度是\(O(m\sqrt n\lg 10^9)\)
实际上这也差不多是正解的时间复杂度……(或许可以过吧……)
正解
题解中用到一个叫划分树的东西,类似于整体二分的过程记录下来,这里就不在赘述了。
它有个经典的用处就是求区间第\(k\)小。
然而这个东西修改很不方便……如果单是修改是\(O(n)\)的(随便想一想就能知道),或者重构,是\(O(n\lg n)\)的。
为了减小代码复杂度,还是考虑重构吧……
考虑按照\(x\)坐标分块。每个块内以\(y\)排序。
每个整块建立一个划分树。
修改的时候直接暴力重构。
查询的时候就是一堆整块和一些散点。散点记录到一个数组里面。
在几个划分树上二分即可(散点也是暴力判断)。
时间复杂度是\(O((n+m)\sqrt n \lg 10^8)\)
如果不重构,加上修改操作,时间复杂度会优一点。
还有,实际上也不需要划分树这种东西,用主席树代替也可以(应该会简单很多)。
代码(未A,待以后填坑)
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cassert>
#define N 60010
#define M 10010
#define MAX 1000000000
#define K 250
#define N_K 250
inline int input(){
char ch=getchar();
while (ch<'0' || '9'<ch)
ch=getchar();
int x=0;
do{
x=x*10+ch-'0';
ch=getchar();
}
while ('0'<=ch && ch<='9');
return x;
}
int n,m;
struct DOT{
int x,y,z;
} d[N];
int p[N],rev[N];
inline bool cmp1(int a,int b){return d[a].x<d[b].x;}
inline bool cmp2(int a,int b){return d[a].y<d[b].y;}
int nb,bel[N],end[N_K],mxx[N_K];
int lis[31][N],num[31][N];
int nq,q[N];
int nt;
struct Range{
int l,r,st,en;
} t[N_K];
void build(int fl,int l,int r,int st,int en){
if (l==r || st>en)
return;
int mid=l+r>>1,k=st-1;
for (int i=st,j=0;i<=en;++i){
if (d[lis[fl][i]].z<=mid){
++j;
lis[fl+1][++k]=lis[fl][i];
}
num[fl][i]=j;
}
for (int i=st;i<=en;++i)
if (d[lis[fl][i]].z>mid)
lis[fl+1][++k]=lis[fl][i];
build(fl+1,l,mid,st,st+num[fl][en]-1);
build(fl+1,mid+1,r,st+num[fl][en],en);
}
int kth(int fl,int l,int r,int k){
if (l==r){
int siz=0;
for (int i=1;i<=nt;++i)
siz+=t[i].r-t[i].l+1;
for (int i=1;i<=nq;++i)
if (q[i]==l)
siz++;
if (k<=siz)
return l;
return -1;
}
int mid=l+r>>1,lsiz=0;
for (int i=1;i<=nt;++i)
lsiz+=num[fl][t[i].r]-(t[i].l>t[i].st?num[fl][t[i].l-1]:0);
for (int i=1;i<=nq;++i)
if (q[i]<=mid)
lsiz++;
if (k<=lsiz){
for (int i=1;i<=nt;++i){
t[i].en=t[i].st+num[fl][t[i].en]-1;
t[i].l=t[i].st+(t[i].l>t[i].st?num[fl][t[i].l-1]:0);
t[i].r=t[i].st+num[fl][t[i].r]-1;
}
return kth(fl+1,l,mid,k);
}
for (int i=1;i<=nt;++i){
int rsiz=t[i].r-t[i].l+1-lsiz;
t[i].r=t[i].r+num[fl][t[i].en]-num[fl][t[i].r];
t[i].l=t[i].r-rsiz+1;
t[i].st=t[i].st+num[fl][t[i].en];
}
for (int i=1;i<=nq;++i)
if (q[i]<=mid)
q[i]=MAX+1;
return kth(fl+1,mid+1,r,k-lsiz);
}
int main(){
//freopen("in.txt","r",stdin);
n=input(),m=input();
for (int i=1;i<=n;++i)
d[i]={input(),input(),input()},p[i]=i;
sort(p+1,p+n+1,cmp1);
for (int i=1;i*K<=n;++i){
++nb;
for (int j=(i-1)*K+1;j<=i*K;++j)
bel[j]=nb;
end[i]=i*K;
}
if (n%K){
++nb;
for (int j=n/K*K+1;j<=n;++j)
bel[j]=nb;
end[nb]=n;
}
for (int i=1;i<=nb;++i){
mxx[i]=d[p[end[i]]].x;
sort(p+end[i-1]+1,p+end[i]+1,cmp2);
for (int j=end[i-1]+1;j<=end[i];++j)
lis[0][j]=p[j];
build(0,0,MAX,end[i-1]+1,end[i]);
}
for (int i=1;i<=n;++i)
rev[p[i]]=i;
while (m--){
char op=getchar();
while (op<'A' || 'Z'<op)
op=getchar();
if (op=='S'){
int a=input()+1,b=input()+1,tmp=d[a].z;
d[a].z=d[b].z;
build(0,0,MAX,end[bel[rev[a]]-1]+1,end[bel[rev[a]]]);
d[b].z=tmp;
build(0,0,MAX,end[bel[rev[b]]-1]+1,end[bel[rev[b]]]);
}
else{
int x0=input(),y0=input(),x1=input(),y1=input(),k=input();
if (x0>x1)
swap(x0,x1);
if (y0>y1)
swap(y0,y1);
int lb=nb+1,rb=0;
for (int i=1;i<=nb;++i)
if (mxx[i]>=x0){
lb=i;
break;
}
for (int i=nb;i>=1;--i)
if (mxx[i]<=x1){
rb=i;
break;
}
if (lb>rb+1){
printf("It doesn't exist.\n");
break;
}
nq=0;
if (lb==rb+1){
for (int i=end[lb-1]+1;i<=end[lb];++i)
if (x0<=d[p[i]].x && d[p[i]].x<=x1 && y0<=d[p[i]].y && d[p[i]].y<=y1)
q[++nq]=d[p[i]].z;
}
else{
for (int i=end[lb-1]+1;i<=end[lb];++i)
if (x0<=d[p[i]].x && d[p[i]].x<=x1 && y0<=d[p[i]].y && d[p[i]].y<=y1)
q[++nq]=d[p[i]].z;
for (int i=end[rb]+1;i<=end[rb+1];++i)
if (x0<=d[p[i]].x && d[p[i]].x<=x1 && y0<=d[p[i]].y && d[p[i]].y<=y1)
q[++nq]=d[p[i]].z;
}
nt=0;
for (int i=lb+1;i<=rb;++i){
int l=end[i-1]+1,r=end[i],st=end[i]+1,en;
while (l<=r){
int mid=l+r>>1;
if (d[p[mid]].y>=y0)
r=(st=mid)-1;
else
l=mid+1;
}
if (st==end[i]+1)
continue;
l=st,r=end[i];
en=st-1;
while (l<=r){
int mid=l+r>>1;
if (d[p[mid]].y<=y1)
l=(en=mid)+1;
else
r=mid-1;
}
if (st>en)
continue;
// assert(st>=0 && en>=0);
t[++nt]={st,en,end[i-1]+1,end[i]};
}
int ans=kth(0,0,MAX,k);
if (ans==-1)
printf("It doesn't exist.\n");
else
printf("%d\n",ans);
}
}
return 0;
}
总结
像这种题,暴力是不好卡掉的……
所以要看准数据范围,加以优秀的卡常操作……
[JZOJ2865]【集训队互测 2012】Attack的更多相关文章
- 洛谷 P4463 - [集训队互测 2012] calc(多项式)
题面传送门 & 加强版题面传送门 竟然能独立做出 jxd 互测的题(及其加强版),震撼震撼(((故写题解以祭之 首先由于 \(a_1,a_2,\cdots,a_n\) 互不相同,故可以考虑求出 ...
- [JZOJ2866] 【集训队互测 2012】Bomb
题目 题目大意 给你一个有\(n\)个点的平面. 选择三个点,求两两之间曼哈顿距离和的最大值和最小值. 思考历程&正解 比赛的时候没有想太多,但感觉似乎比较水-- 首先有个很显然的性质,答案为 ...
- P4463 [集训队互测2012] calc 拉格朗日插值 dp 多项式分析
LINK:calc 容易得到一个nk的dp做法 同时发现走不通了 此时可以考虑暴力生成函数. 不过化简那套不太熟 且最后需要求多项式幂级数及多项式exp等难写的东西. 这里考虑观察优化dp的做法. 不 ...
- 【loj2461】【2018集训队互测Day 1】完美的队列
#2461. 「2018 集训队互测 Day 1」完美的队列 传送门: https://loj.ac/problem/2461 题解: 直接做可能一次操作加入队列同时会弹出很多数字,无法维护:一个操作 ...
- 【2018集训队互测】【XSY3372】取石子
题目来源:2018集训队互测 Round17 T2 题意: 题解: 显然我是不可能想出来的……但是觉得这题题解太神了就来搬(chao)一下……Orzpyz! 显然不会无解…… 为了方便计算石子个数,在 ...
- UOJ#191. 【集训队互测2016】Unknown 点分治 分治 整体二分 凸包 计算几何
原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ191.html 题目传送门 - UOJ191 题意 自行移步集训队论文2016中罗哲正的论文. 题解 自行 ...
- LOJ3069. 「2019 集训队互测 Day 1」整点计数(min_25筛)
题目链接 https://loj.ac/problem/3069 题解 复数真神奇. 一句话题意:令 \(f(x)\) 表示以原点 \((0, 0)\) 为圆心,半径为 \(x\) 的圆上的整点数量, ...
- UOJ#191. 【集训队互测2016】Unknown
题意:维护一个数列,每个元素是个二维向量,每次可以在后面加一个元素或者删除一个元素.给定P(x,y),询问对于[l,r]区间内的元素$S_i$,$S_i \times P$的最大值是多少. 首先简单地 ...
- 【集训队互测2015】Robot
题目描述 http://uoj.ac/problem/88 题解 维护两颗线段树,维护最大值和最小值,因为每次只有单点查询,所以可以直接在区间插入线段就可以了. 注意卡常,不要写STL,用链表把同类修 ...
随机推荐
- 商城sku的选择功能--客户端
前段时间,刚好做到了有关sku这个功能.客户端的sku,和后台管理系统的sku.当初查了大量资料,遂做个记录,以免忘记. 这篇先写客户端的sku功能把,类似于去淘宝京东等购物,就会有个规格让你选择.如 ...
- [BOI2009]Radio Transmission 无线传输
题目描述 给你一个字符串,它是由某个字符串不断自我连接形成的. 但是这个字符串是不确定的,现在只想知道它的最短长度是多少. 输入输出格式 输入格式: 第一行给出字符串的长度,1 < L ≤ 1, ...
- 第五篇 scrapy安装及目录结构,启动spider项目
实际上安装scrapy框架时,需要安装很多依赖包,因此建议用pip安装,这里我就直接使用pycharm的安装功能直接搜索scrapy安装好了. 然后进入虚拟环境创建一个scrapy工程: (third ...
- 枚举加countdownLatch的使用
package com.cxy.juc; import java.util.concurrent.CountDownLatch; public class CountDownlatchDemo { p ...
- pytest-文件名类名方法名执行部分用例
pytest test_class_01.py 执行文件名 pytest -v -s test_class_01.py 执行文件名 pytest -v test_class_01.py::TestCl ...
- MVC--MVP?
第一部分:什么是MVP?什么是MVC? 1.什么是MVP? M:数据层(数据库.网络.文件存储等等...) V:View和Activity和Fragment以及它们的子类 P:中介->Prese ...
- Google Fuchsia
Fuchsia是Google开发的操作系统[1].和以前Google开发的操作系统,如基于Linux内核的Chrome OS和Android等不同,Fuchsia基于新的名为Zircon的微内核[2] ...
- js用正则判断身份证号码
在用户注册或修改信息时会用到正则表达式判断身份证号,直接调用该函数即可 //判断身份证号码 function idCardFn(idCard){ }(||)?\d{}([-]|[])([-]|[]\d ...
- 关于springboot错误:“找不到或无法加载主类”的解决办法
我从网上找的一个Demo,运行的时候报 错误:“找不到或无法加载主类”,百度了一番,都是说在项目目录打开cmd,使用 mvn install.mvn clean complie之类的命令,都成功了,但 ...
- Go const 关键字
Go const 关键字 package main import "fmt" func main() { const LENGTH int = 10 const WIDTH int ...