题目

题目大意

平面上有一堆带权值的点。两种操作:交换两个点的权值,查找一个矩形的第\(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的更多相关文章

  1. 洛谷 P4463 - [集训队互测 2012] calc(多项式)

    题面传送门 & 加强版题面传送门 竟然能独立做出 jxd 互测的题(及其加强版),震撼震撼(((故写题解以祭之 首先由于 \(a_1,a_2,\cdots,a_n\) 互不相同,故可以考虑求出 ...

  2. [JZOJ2866] 【集训队互测 2012】Bomb

    题目 题目大意 给你一个有\(n\)个点的平面. 选择三个点,求两两之间曼哈顿距离和的最大值和最小值. 思考历程&正解 比赛的时候没有想太多,但感觉似乎比较水-- 首先有个很显然的性质,答案为 ...

  3. P4463 [集训队互测2012] calc 拉格朗日插值 dp 多项式分析

    LINK:calc 容易得到一个nk的dp做法 同时发现走不通了 此时可以考虑暴力生成函数. 不过化简那套不太熟 且最后需要求多项式幂级数及多项式exp等难写的东西. 这里考虑观察优化dp的做法. 不 ...

  4. 【loj2461】【2018集训队互测Day 1】完美的队列

    #2461. 「2018 集训队互测 Day 1」完美的队列 传送门: https://loj.ac/problem/2461 题解: 直接做可能一次操作加入队列同时会弹出很多数字,无法维护:一个操作 ...

  5. 【2018集训队互测】【XSY3372】取石子

    题目来源:2018集训队互测 Round17 T2 题意: 题解: 显然我是不可能想出来的……但是觉得这题题解太神了就来搬(chao)一下……Orzpyz! 显然不会无解…… 为了方便计算石子个数,在 ...

  6. UOJ#191. 【集训队互测2016】Unknown 点分治 分治 整体二分 凸包 计算几何

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ191.html 题目传送门 - UOJ191 题意 自行移步集训队论文2016中罗哲正的论文. 题解 自行 ...

  7. LOJ3069. 「2019 集训队互测 Day 1」整点计数(min_25筛)

    题目链接 https://loj.ac/problem/3069 题解 复数真神奇. 一句话题意:令 \(f(x)\) 表示以原点 \((0, 0)\) 为圆心,半径为 \(x\) 的圆上的整点数量, ...

  8. UOJ#191. 【集训队互测2016】Unknown

    题意:维护一个数列,每个元素是个二维向量,每次可以在后面加一个元素或者删除一个元素.给定P(x,y),询问对于[l,r]区间内的元素$S_i$,$S_i \times P$的最大值是多少. 首先简单地 ...

  9. 【集训队互测2015】Robot

    题目描述 http://uoj.ac/problem/88 题解 维护两颗线段树,维护最大值和最小值,因为每次只有单点查询,所以可以直接在区间插入线段就可以了. 注意卡常,不要写STL,用链表把同类修 ...

随机推荐

  1. B. Light bulbs(2019 ICPC上海站)

    There are NN light bulbs indexed from 00 to N-1N−1. Initially, all of them are off. A FLIP operation ...

  2. webpack4 入门配置研究

    1. 全局安装 npm install webpack webpack-cli webpack-dev-server -g 1.1)输密文的密码(电脑开机) 1.2)安装成功 2. 输入命令mkdir ...

  3. 2018-12-21-微软最具价值专家-MVP-如何获得-Resharper-的免费功能

    title author date CreateTime categories 微软最具价值专家 MVP 如何获得 Resharper 的免费功能 lindexi 2018-12-21 11:29:0 ...

  4. RHEL5/6/7中常用命令及命令之间的差异

    System basics Task RHEL5 RHEL6 RHEL7 View subscription information /etc/sysconfig/rhn/systemid /etc/ ...

  5. 深入研究js中的位运算及用法

    什么是位运算? 位运算是在数字底层(即表示数字的 32 个数位)进行运算的.由于位运算是低级的运算操作,所以速度往往也是最快的(相对其它运算如加减乘除来说),并且借助位运算有时我们还能实现更简单的程序 ...

  6. js排他功能示例

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  7. Wordpress 文章编辑页面添加 metabox

    add_meta_box($id,$title,$callback,$screen,$context:,$priority); 参数 $id (字符串)(必需)Meta模块的 HTML"ID ...

  8. Maven将本地项目打包后引入本地另一个项目

    进入需要打包的文件夹,执行:mvn clean install -X 生成JAR包 打完JAR包后,将maven依赖安装 执行命令: install:install-file -Dfile=E:\co ...

  9. QueryList getData()方法中多次调用来实现递归多级采集。

    <?php require 'QueryList/vendor/autoload.php'; use QL\QueryList; //获取每个li里面的h3标签内容,和class为item的元素 ...

  10. 良田高拍仪集成vue项目

    一.硬件及开发包说明: 产品型号为良田高拍仪S1800A3,集成b/s系统,适用现代浏览器,图片使用BASE64数据.开发包的bin文件下的video.flt文件需要和高拍仪型号的硬件id对应,这个可 ...