题意

  有一个 \(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. systemctl stop 列表

    root@devstack2019:/etc/keystone# systemctl stop Display all 201 possibilities? (y or n)accounts-daem ...

  2. 【Linux】【一】linux 目录切换、创建目录和文件、编辑目录以及文件(txt)

    以下 是在指定目录下创建文件夹目录,以及在该目录下创建txt文件进行编辑,保存. 然后删除相关文件以及目录的命令操作记录. 本操作记录中的命令简单解释: pwd 显示当前路径 ls 显示当前目录下的文 ...

  3. C语言基础:递归函数,全局(局)变量

    #include <stdio.h>int factorial(int a); int Fibonacci(a);long Hanoi(a); void main(){ } 函数递归调用: ...

  4. Spring MVC模式下,获取WebApplicationContext的工具类 方法

    在已有的注解类型下,获取WebApplicationContext的工具类 通过  WebApplicationContextUtils.getRequiredWebApplicationContex ...

  5. 【HANA系列】SAP HANA SQL计算两个日期的差值

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[HANA系列]SAP HANA SQL计算两个 ...

  6. caoz的梦呓:创业公司如何做好信息安全

    猫宁!!! 参考链接:https://mp.weixin.qq.com/s/gCWjzHBRfbPFhNeg2VtFhA https://mp.weixin.qq.com/s/bmifCmD2CHV1 ...

  7. @autowired、@Qualifier、@Primary注解

    @autowired 可以自动帮你把Bean里面引用的对象的setter/getter方法省略,自动帮你set/get. 启动spring IoC时,容器自动装载了一个AutowiredAnnotat ...

  8. Oracle 即时客户点下载以及简单连接数据库的方法

    1. 下载方法 百度 oracle client 第一个即可 2. 下载地址为: https://www.oracle.com/database/technologies/instant-client ...

  9. 小记---------spark优化之更优分配资源

      spark优化:在一定范围之内,增加资源与性能的提升是成正比的. 因此,       一个cpu core  执行一个task线程. task数: 若有 cpu core 2个.num-execu ...

  10. Go语言中 Print,Println 和 Printf 的区别(八)

    Print 和 Println 这两个打印方式类似,只在格式上有区别 1. Println 打印的每一项之间都会有空行,Print 没有,例如: fmt.Println("go", ...