【hdu 6089】Rikka with Terrorist
题意
有一个 \(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的更多相关文章
- 【hdu 5632】Rikka with Array
Description As we know, Rikka is poor at math. Yuta is worrying about this situation, so he gives Ri ...
- 【数位dp】【HDU 3555】【HDU 2089】数位DP入门题
[HDU 3555]原题直通车: 代码: // 31MS 900K 909 B G++ #include<iostream> #include<cstdio> #includ ...
- 【HDU 5647】DZY Loves Connecting(树DP)
pid=5647">[HDU 5647]DZY Loves Connecting(树DP) DZY Loves Connecting Time Limit: 4000/2000 MS ...
- -【线性基】【BZOJ 2460】【BZOJ 2115】【HDU 3949】
[把三道我做过的线性基题目放在一起总结一下,代码都挺简单,主要就是贪心思想和异或的高斯消元] [然后把网上的讲解归纳一下] 1.线性基: 若干数的线性基是一组数a1,a2,a3...an,其中ax的最 ...
- 【HDU 2196】 Computer(树的直径)
[HDU 2196] Computer(树的直径) 题链http://acm.hdu.edu.cn/showproblem.php?pid=2196 这题可以用树形DP解决,自然也可以用最直观的方法解 ...
- 【HDU 2196】 Computer (树形DP)
[HDU 2196] Computer 题链http://acm.hdu.edu.cn/showproblem.php?pid=2196 刘汝佳<算法竞赛入门经典>P282页留下了这个问题 ...
- 【HDU 5145】 NPY and girls(组合+莫队)
pid=5145">[HDU 5145] NPY and girls(组合+莫队) NPY and girls Time Limit: 8000/4000 MS (Java/Other ...
- 【hdu 1043】Eight
[题目链接]:http://acm.hdu.edu.cn/showproblem.php?pid=1043 [题意] 会给你很多组数据; 让你输出这组数据到目标状态的具体步骤; [题解] 从12345 ...
- 【HDU 3068】 最长回文
[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=3068 [算法] Manacher算法求最长回文子串 [代码] #include<bits/s ...
随机推荐
- linux常用命令(10)more命令
more命令,功能类似 cat ,cat命令是整个文件的内容从上到下显示在屏幕上. more会以一页一页的显示方便使用者逐页阅读,而最基本的指令就是按空白键(space)就往下一页显示,按 b 键就会 ...
- 【转】Apache HBase 问题排查思路
[From]https://www.itcodemonkey.com/article/9426.html HBCK - HBCK检查什么? (1)HBase Region一致性 集群中所有region ...
- 非UI线程更新UI界面的各种方法小结
转载:https://www.cnblogs.com/xiashengwang/archive/2012/08/18/2645541.html 我们知道只有UI线程才能更新UI界面,其他线程访问UI控 ...
- sql 死锁查看
--每秒死锁数量 SELECT * FROM sys.dm_os_performance_counters WHERE counter_name LIKE 'Number of Deadlocksc% ...
- 如何在VUE中使用leaflet地图框架
前言:在leaflet的官方文档只有静态的HTML演示并没有结合VUE的demo 虽然也有一些封装好的leaflet库例如Vue-Leaflet,但是总感觉用起来不是那么顺手,有些业务操作还是得用l ...
- 手写一个简单到SpirngMVC框架
spring对于java程序员来说,无疑就是吃饭到筷子.在每次编程工作到时候,我们几乎都离不开它,相信无论过去,还是现在或是未来到一段时间,它仍会扮演着重要到角色.自己对spring有一定的自我见解, ...
- 分享个昨天学的,sqlserver查表的所有列的列名,类型,长度的sql
select a.name as 列名, a.length as 长度,b.name as 类型 from syscolumns a left join systypes b on a.xtype ...
- [转帖]docker-compose
docker-compose https://www.cnblogs.com/embedded-linux/p/10714179.html 需要学习使用一下. 改天自己再改改用过的yaml文件. ...
- python控制流 -- if、for、while、range()、sys.exit()
1.布尔值 “布尔”数据类型只有两种:True和False #首字母以T或F开头,后面小写,且不能作为变量赋值 2.比较操作符 == 等于 != 不等于 < 小于 > 大于 &l ...
- 小记--------spark内核架构原理分析
首先会将jar包上传到机器(服务器上) 1.在这台机器上会产生一个Application(也就是自己的spark程序) 2.然后通过spark-submit(shell) 提交程序 ...