题意

  有一个 \(n\times m\) 的二维网格,其中有 \(k\) 个禁止点。

  有 \(q\) 组询问,每组询问为给一个点,求有多少个矩形以这个点为一角且不包含禁止点。

  \(n,m,k,q\le 10^5\)

sol

  zjt 是怎么认为这是道李超树题的……难道只是因为看到了官方题解吗?

  这题不难,但是太恶心了我琢磨了三小时,关键是没选手写题解,官方题解还写李超树误导人

  不过官方题解除了最后一段最后一句外,其余内容还是可以借鉴的:

  问题相当于平面中有若干障碍点,询问以某一个点为四个角之一的不包含障碍点的矩形有多少个。

  对每一组询问,维护数组 \(U,D\),\(U_i\) 表示所有在询问点上方的障碍点中,横坐标为 \(i\) 的纵坐标最小值,\(D_i\) 表示所有在询问点下方的障碍点中,横坐标在 \(i\) 的纵坐标最大值。那么枚举矩形横坐标范围的另一端 \(j\),满足条件的矩形有 \(\min_{k∈[i,j]} U_k - \max_{k∈[i,j]} D_k − 1\) 个。

  离线,按照纵坐标从小到大枚举询问,因为 \(U\) 和 \(D\) 只会修改 \(O(K)\) 次,所以可以用线段树维护。之后要考虑的就是求 \(\sum\limits_{j=1}^{n} \min_{k∈[i,j]} U_k\) 和 \(\sum\limits_{j=1}^{n} \max_{k∈[i,j]} D_k\)。这个是李超线段树的经典问题,可以在 \(O(n\log^2n)\) 的时间复杂度内解决。

  离线是个很好的思路。

  然后我们再思考一下,发现官方题解是把原问题拆成了上下两部分求解,但我们可以把原问题拆成四个象限求解,每次把坐标系旋转 \(90°\),然后对同一个象限求解(可以是任意象限,只要四次求解的象限相同就行)。本文处理的是最好维护的第三象限。

  把所有点以横坐标为第一关键字,纵坐标为第二关键字,从小到大排序。

  考虑一次性从下往上处理一列的所有点。一个询问点在某一个象限的答案大概就是图中的紫色区域:



  点表示询问点,十字表示禁止点。

  则图中紫色区域就是红色询问点点在一个象限的答案。

  则我们需要动态维护当前询问点所在列的下方离他最近的禁止点的位置。

  然后求出红色矩形的面积,减去左边那些禁止点组成的类似于“上凸包”的面积。

  “上凸包”的面积就是每个前缀的 所有后缀区间最大值的和

  问题变成了如何用线段树动态维护 以每个点为结尾的所有后缀区间最大值的和。

  线段树上每个节点用一个变量 \(sum\) 记录该区间所有后缀区间最大值的和。不难发现在叶子结点处,这个值很好得到,我们考虑怎么把它从两个儿子合并到父亲。

  不难发现,右儿子的 \(sum\) 可以直接加到父亲上,而左儿子由于记得是以父亲区间中点为结尾的 \(sum\),而我们需要左儿子以父亲区间右端点(即右儿子右端点)为后缀结尾,所以我们重新计算一下右儿子对左儿子的影响,得到左儿子对父亲的贡献。

  不难发现,把左儿子的所有后缀的右端点 延长到右儿子的右端后,右儿子的最大值会对左儿子的某个后缀区域造成影响。那这个后缀区域的左端点是哪呢?设右儿子最大值为 \(x\),显然就是左儿子从右往左数第一个 \(\ge x\) 的位置。这个位置右边的数都会因后缀右端点延长,要与 \(x\) 取 \(\max\) 而被推平成 \(x\),这个位置及其左边的数则都不会受影响。

  所以我们在线段树的每个节点再维护一个区间 \(\max\)。在 \(pushup\) 更新父亲的 \(sum\) 时,在左子树内进行二分,找到最右边的 \(\ge x\) 的位置,若往右子树走则累加 \(sum[cur]-sum[rson]\)(就是左子树的答案),若往左子树走则累加 \(x\times (r-mid)\)(就是右子树被推平成 \(x\) 了)。

  查询时要查询一个区间的 \(sum\) 和。对于该区间在线段树上拆得的 \(\log\) 个区间,用类似于 \(pushup\) 的方法从右往左合并这些区间,显然合并两个区间并不要求这两个区间等长。

  注意在处理一列时,先做完这列所有点的查询,再用这列所有点更新线段树。

  还有一个需要注意的细节是统计一个询问点的答案时,不要考虑它的行左,因为它的列下已经被考虑上了,转四次坐标系之后会发现询问点所在的行左、行右、列上、列下都恰好被算了一次,这样就避免处理了重复计算的问题。

  总之就是个快乐线段树题。时间复杂度 \(O(n\log^2{n})\),因为每次 \(pushup\) 都要在子树内做一次线段树二分。

#include<bits/stdc++.h>
#define ll long long
#define N 100010
#define lc o<<1
#define rc o<<1|1
#define fi first
#define se second
using namespace std;
typedef pair<ll,int> pr;
inline int read(){
int x=0; bool f=1; char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
if(f) return x;
return 0-x;
}
int nn,mm,k,q;
struct Point{
int x,y,id;
Point(){}
Point(int a, int b, int c):x(a), y(b), id(c){}
inline bool operator < (const Point &a)const{
return x!=a.x ? x<a.x : y<a.y;
}
}a[N<<1];
namespace SegTree{
struct Tree{int mx; ll sum;}tr[N<<2];
void build(int o, int l, int r){
tr[o].mx=tr[o].sum=0;
if(l==r) return;
int mid=l+r>>1;
build(lc,l,mid), build(rc,mid+1,r);
}
ll go(int o, int l, int r,int v){
if(l==r) return max(tr[o].mx,v);
int mid=l+r>>1;
if(tr[rc].mx>=v) return tr[o].sum-tr[rc].sum+go(rc,mid+1,r,v);
else return go(lc,l,mid,v)+(ll)v*(r-mid);
}
inline void pushup(int o, int l, int r){
int mid=l+r>>1;
tr[o].sum = tr[rc].sum + go(lc,l,mid,tr[rc].mx);
tr[o].mx = max(tr[lc].mx, tr[rc].mx);
}
void upd(int o, int l, int r, int x, int v){
if(l==r){tr[o].mx=tr[o].sum=v; return;}
int mid=l+r>>1;
if(x<=mid) upd(lc,l,mid,x,v);
else upd(rc,mid+1,r,x,v);
pushup(o,l,r);
}
pr query(int o, int l, int r, int L, int R, int v){
if(L<=l && r<=R) return pr(go(o,l,r,v), max(v,tr[o].mx));
int mid=l+r>>1;
if(R<=mid) return query(lc,l,mid,L,R,v);
if(mid<L) return query(rc,mid+1,r,L,R,v);
pr b = query(rc,mid+1,r,L,R,v);
pr a = query(lc,l,mid,L,R,b.se);
return pr(a.fi+b.fi, a.se);
}
}using namespace SegTree;
void rotate(int n){
for(int i=1; i<=n; ++i){
int x=mm-a[i].y+1, y=a[i].x;
a[i].x=x, a[i].y=y;
}
swap(nn,mm);
}
int now[N];
ll ans[N];
void solve(int n){
rotate(n);
build(1,1,mm);
sort(a+1,a+n+1);
memset(now,0,sizeof now);
for(int i=1,lst=0; i<=n; i=lst+1){
if(a[i].x!=a[i-1].x){
lst=i;
while(a[lst].x==a[lst+1].x) ++lst;
int l=0;
for(int j=i; j<=lst; ++j)
if(!a[j].id) l=a[j].y;
else if(l+1<=a[j].y-1)
ans[a[j].id] += (ll)a[j].x*(a[j].y-l-1) - query(1,1,mm,l+1,a[j].y-1,now[a[j].y]).fi;
for(int j=i; j<=lst; ++j)
if(!a[j].id)
upd(1,1,mm,a[j].y,a[j].x),
now[a[j].y]=a[j].x;
}
}
}
int main(){
nn=read(), mm=read(), k=read(), q=read();
int x,y;
for(int i=1; i<=k; ++i){
x=read(), y=read();
a[i]=Point(x,y,0);
}
for(int i=1; i<=q; ++i){
x=read(), y=read();
a[k+i]=Point(x,y,i);
}
for(int i=1; i<=4; ++i) solve(k+q);
for(int i=1; i<=q; ++i) printf("%lld\n",ans[i]+1);
return 0;
}
/*
19 19 20 19 9 11
12 11
8 3
10 2
11 2
18 8
10 6
16 11
13 9
13 8
8 7
2 6
5 7
7 18
6 5
16 15
17 14
15 1
2 4
3 3 10 10
15 17
8 17
6 9
16 2
5 15
17 4
4 3
4 14
9 6
19 16
14 4
7 11
14 15
4 1
14 14
3 11
9 19
15 15
*/

【hdu 6089】Rikka with Terrorist的更多相关文章

  1. 【hdu 5632】Rikka with Array

    Description As we know, Rikka is poor at math. Yuta is worrying about this situation, so he gives Ri ...

  2. 【数位dp】【HDU 3555】【HDU 2089】数位DP入门题

    [HDU  3555]原题直通车: 代码: // 31MS 900K 909 B G++ #include<iostream> #include<cstdio> #includ ...

  3. 【HDU 5647】DZY Loves Connecting(树DP)

    pid=5647">[HDU 5647]DZY Loves Connecting(树DP) DZY Loves Connecting Time Limit: 4000/2000 MS ...

  4. -【线性基】【BZOJ 2460】【BZOJ 2115】【HDU 3949】

    [把三道我做过的线性基题目放在一起总结一下,代码都挺简单,主要就是贪心思想和异或的高斯消元] [然后把网上的讲解归纳一下] 1.线性基: 若干数的线性基是一组数a1,a2,a3...an,其中ax的最 ...

  5. 【HDU 2196】 Computer(树的直径)

    [HDU 2196] Computer(树的直径) 题链http://acm.hdu.edu.cn/showproblem.php?pid=2196 这题可以用树形DP解决,自然也可以用最直观的方法解 ...

  6. 【HDU 2196】 Computer (树形DP)

    [HDU 2196] Computer 题链http://acm.hdu.edu.cn/showproblem.php?pid=2196 刘汝佳<算法竞赛入门经典>P282页留下了这个问题 ...

  7. 【HDU 5145】 NPY and girls(组合+莫队)

    pid=5145">[HDU 5145] NPY and girls(组合+莫队) NPY and girls Time Limit: 8000/4000 MS (Java/Other ...

  8. 【hdu 1043】Eight

    [题目链接]:http://acm.hdu.edu.cn/showproblem.php?pid=1043 [题意] 会给你很多组数据; 让你输出这组数据到目标状态的具体步骤; [题解] 从12345 ...

  9. 【HDU 3068】 最长回文

    [题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=3068 [算法] Manacher算法求最长回文子串 [代码] #include<bits/s ...

随机推荐

  1. iOS限制输入解决方法

    关于iOS 键盘输入限制(只能输入字母,数字,禁止输入特殊符号): 方法一: 直接限制输入 - (void)viewDidLoad { [super viewDidLoad]; textField = ...

  2. selenium+java:获取列表中的值

    selenium+java:获取列表中的值 (2011-08-23 17:14:48) 标签: 杂谈 分类: selenium 初步研究利用java+testNg框架下写selenium测试用例,今天 ...

  3. OpenCV-Python画直方图和累积直方图

    代码如下: import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread('C:\\Users\\adm ...

  4. C#编程 线程,任务和同步(2) 开启线程

    创建线程的几种方法: 1 异步委托 创建线程的一种简单方式是定义一个委托,并异步调用它. 委托是方法的类型安全的引用.Delegate类 还支持异步地调用方法.在后台,Delegate类会创建一个执行 ...

  5. Spring boot (一):入门篇

    Spring boot 简介 Build Anything with Spring Boot:Spring Boot is the starting point for building all Sp ...

  6. pickle.dump()和pickle.load()

    python的pickle模块实现了基本的数据序列和反序列化. 通过pickle模块的序列化操作我们能够将程序中运行的对象信息保存到文件中去,永久存储: 通过pickle模块的反序列化操作,我们能够从 ...

  7. Ubuntu下c程序运行原理

    运行环境: 1.Vitural box下安装Ubuntu虚拟机 2.编译运行工具:gcc 3.编辑器:vim 主要过程: 1.利用vim编辑hello.c 2.编译和执行 (1)预处理阶段:使用 -E ...

  8. 通达信金融终端_尘缘整合_V7.12

    http://pan.baidu.com/s/1gvtPO http://pan.baidu.com/s/1xqrk6 通达信金融终端_尘缘整合_V7.12

  9. 论文阅读 | Generating Fluent Adversarial Examples for Natural Languages

    Generating Fluent Adversarial Examples for Natural Languages   ACL 2019 为自然语言生成流畅的对抗样本 摘要 有效地构建自然语言处 ...

  10. Laravel 查询&数据库&模型

    1.with()与load区别: 都称为 延迟预加载,不同点在于 load()是在已经查询出来的模型上调用,而 with() 则是在 ORM 查询构造器上调用. Order::query()-> ...