题面传送门

题意:

有一个 \(10^6\times 10^6\) 的地图。其中 \(m\) 个位置上有花,\(f\) 个矩形外围用栅栏围了起来。保证 \(f\) 个矩形两两之间没有公共点。

\(q\) 组询问,每组询问给出两个整数 \(x,y\),求出:

  • 从点 \((x,y)\) 出发,只能向下或向右走,不能越过栅栏,总共可以摘到多少朵花。

\(0\leq m,f,q\leq 2\times 10^5\)

先考虑 \(f=0\) 的情况,那就是一个弱智的扫描线。从低往高扫,如果 \((x_i,y_i)\) 有花,那就在 \(y_i\) 位置 \(+1\)。单点修改,区间查询,线段树可以实现。

再考虑 \(f\neq 0\) 的情况,大体思路还是扫描线,不过实现起来细节多了很多。

当我们扫描到一个矩形 \((x_1,y_1),(x_2,y_2)\) 的下边界的时候,我们只需把线段树上 \([y_1,y_2]\) 全部改为 \(0\),因为这些位置都没有办法再向下走了。

当我们遇到一个有花的位置的时候,比如下图:

如果橘色位置有花,那么这朵花会对这一行绿色位置产生 \(1\) 的贡献。

故如果一朵花的纵坐标为 \(y\),那么所有纵坐标在 \([pre_y+1,y]\) 的位置的答案都会加 \(1\)。其中 \(pre_y\) 为 \(y\) 之前的墙的位置。

比较麻烦的是遇到矩形的上边界。

还是拿张图来举个例子:

比如说我们现在遇到了右边这个矩形 \((x_1,y_1),(x_2,y_2)\) 的上边界。首先 \([y_1,y_2]\) 内(绿色格子)的位置的答案肯定要发生变化,原先我们计算的是矩形内的答案,现在我们要计算矩形外的答案。稍微观察一下就能发现,这些绿色格子上的答案都等于黄色格子的答案,

除此之外,还有一些格子的值要变化。例如上图中的红色格子,相比于它下方的粉色格子,它既可以向右走,也可以向下走,而从粉色格子只能向下走。多出来的部分就是向右走可以摘到的花朵数。

如果不计当前这一行的贡献的话,那么向右走可以摘到的花朵数就是黄色格子的答案。

但这样算会有重复,有的花既可以通过向下走摘到,也可以通过向右走摘到。这样的花朵的个数其实就是灰色格子的答案。

故红色格子上的答案 \(=\) 粉色格子上的答案 \(+\) 黄色格子上的答案 \(-\) 灰色格子上的答案。

左边三个深蓝色的格子也是如此。

具体来说,假如黄色格子上的答案为 \(x\),灰色格子上的答案为 \(y\),那么碰到上边界就需执行以下三个操作:

  • \([y_1,y_2]\) 赋上 \(0\)
  • \([pre_{y_1-1},y_2]\) 加上 \(x-y\)
  • \([y_1,y_2]\) 加上 \(y\)

至于怎样求灰色格子的答案,就直接在扫描到矩阵下边界的时候开个 \(tmp\) 数组记录一下就可以了。

于是这题就分析完了。实现起来还有一个细节要注意:就是你扫描到某一行的时候,执行操作的顺序要注意:一定是先加/删除矩阵,再插入花朵,最后解决询问。加/删除矩阵的顺序就按坐标从小到大排个序。我因此一直 WA 3,花了不少时间调程序。

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fz(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define ffe(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=1;
while(!isdigit(c)){if(c=='-') neg=-1;c=getchar();}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
x*=neg;
}
const int MAXL=1e6;
const int MAXN=2e5;
struct event{
int opt,x,y,id;
event(){opt=x=y=id=-1;}
event(int _opt,int _x,int _y,int _id){
opt=_opt;x=_x;y=_y;id=_id;
}
friend bool operator <(event lhs,event rhs){
if((lhs.opt>2||rhs.opt>2)&&lhs.opt!=rhs.opt) return lhs.opt<rhs.opt;
return lhs.x<rhs.x;
}
};
vector<event> g[MAXL+5];
int F,M,Q;
struct node{
int l,r,val,lz;
bool zero;
} s[MAXL*4+5];
void build(int k,int l,int r){
s[k].l=l;s[k].r=r;if(l==r) return;
int mid=(l+r)>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}
void pushdown(int k){
if(s[k].zero){
s[k<<1].zero=s[k<<1|1].zero=1;
s[k<<1].val=s[k<<1|1].val=s[k].val;
s[k<<1].lz=s[k<<1|1].lz=s[k].lz;
s[k].zero=s[k].lz=0;
}
if(s[k].lz){
s[k<<1].val+=s[k].lz;s[k<<1].lz+=s[k].lz;
s[k<<1|1].val+=s[k].lz;s[k<<1|1].lz+=s[k].lz;
s[k].lz=0;
}
}
void modify(int k,int l,int r,int x){
if(l<=s[k].l&&s[k].r<=r){
s[k].val+=x;s[k].lz+=x;return;
} pushdown(k);int mid=(s[k].l+s[k].r)>>1;
if(r<=mid) modify(k<<1,l,r,x);
else if(l>mid) modify(k<<1|1,l,r,x);
else modify(k<<1,l,mid,x),modify(k<<1|1,mid+1,r,x);
s[k].val=max(s[k<<1].val,s[k<<1|1].val);
}
void assign(int k,int l,int r){
if(l<=s[k].l&&s[k].r<=r){
s[k].val=0;s[k].lz=0;s[k].zero=1;return;
} pushdown(k);int mid=(s[k].l+s[k].r)>>1;
if(r<=mid) assign(k<<1,l,r);
else if(l>mid) assign(k<<1|1,l,r);
else assign(k<<1,l,mid),assign(k<<1|1,mid+1,r);
s[k].val=max(s[k<<1].val,s[k<<1|1].val);
}
int query(int k,int x){
if(s[k].l==s[k].r) return s[k].val;
pushdown(k);int mid=(s[k].l+s[k].r)>>1;
if(x<=mid) return query(k<<1,x);
else return query(k<<1|1,x);
}
int ans[MAXN+5],tmp[MAXN+5];
int main(){
scanf("%d",&F);
for(int i=1;i<=F;i++){
int x1,y1,x2,y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
g[x1-1].pb(event(1,y1,y2,i));
g[x2].pb(event(2,y1,y2,i));
}
scanf("%d",&M);
for(int i=1;i<=M;i++){
int x,y;scanf("%d%d",&x,&y);
g[x].pb(event(3,y,-1,-1));
}
scanf("%d",&Q);
for(int i=1;i<=Q;i++){
int x,y;scanf("%d%d",&x,&y);
g[x].pb(event(4,y,-1,i));
}
multiset<int> wall;wall.insert(-1);
build(1,0,MAXL+1);
for(int i=1;i<=MAXL;i++) sort(g[i].begin(),g[i].end());
// modify(1,1,3,1);modify(1,2,4,1);assign(1,3,3);modify(1,1,5,1);
// modify(1,2,6,1);assign(1,2,4);modify(1,4,5,1);
// for(int i=1;i<=6;i++) printf("%d\n",query(1,i));
for(int i=MAXL;i;i--){
for(int j=0;j<g[i].size();j++){
if(g[i][j].opt==1){
assign(1,g[i][j].x,g[i][j].y);
wall.erase(wall.find(g[i][j].x-1));
wall.erase(wall.find(g[i][j].y));
int x=query(1,g[i][j].y+1);
modify(1,(*--wall.lower_bound(g[i][j].x))+1,g[i][j].y,x-tmp[g[i][j].id]);
modify(1,g[i][j].x,g[i][j].y,tmp[g[i][j].id]);
} else if(g[i][j].opt==2){
assign(1,g[i][j].x,g[i][j].y);
wall.insert(g[i][j].x-1);
wall.insert(g[i][j].y);
tmp[g[i][j].id]=query(1,g[i][j].y+1);
} else if(g[i][j].opt==3){
// printf("%d %d %d\n",(*--wall.lower_bound(g[i][j].x))+1,g[i][j].x,1);
modify(1,(*--wall.lower_bound(g[i][j].x))+1,g[i][j].x,1);
} else {
ans[g[i][j].id]=query(1,g[i][j].x);
// printf("%d\n",g[i][j].x);
}
}
}
// for(int i=1;i<=F;i++) printf("%d\n",tmp[i]);
for(int i=1;i<=Q;i++) printf("%d\n",ans[i]);
return 0;
}
/*
4
2 2 8 4
1 9 4 10
6 7 9 9
3 3 7 3
9
3 4
8 4
11 5
10 7
10 8
9 8
2 8
4 11
9 11
8
1 1
5 10
6 9
3 7
7 1
4 2
7 5
3 3
*/

Codeforces Gym 101480C - Cow Confinement(扫描线+线段树)的更多相关文章

  1. Codeforces Gym 100513F F. Ilya Muromets 线段树

    F. Ilya Muromets Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100513/probl ...

  2. Gym - 101982F Rectangles (扫描线+线段树)

    链接:http://codeforces.com/gym/101982/attachments 思路: 问被覆盖次数为奇数次的矩阵的面积并 扫描线求矩阵面积并我们是上界赋为-1,下界赋为1,因为要求覆 ...

  3. Codeforces 1396D - Rainbow Rectangles(扫描线+线段树)

    Codeforces 题面传送门 & 洛谷题面传送门 一道鸽了整整一年的题目,上一次提交好像是 2020 年 9 月 13 日来着的(?) 乍一看以为第 2 个提交和第 3 个提交只差了 43 ...

  4. HDU 3642 - Get The Treasury - [加强版扫描线+线段树]

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3642 Time Limit: 10000/5000 MS (Java/Others) Memory L ...

  5. 【BZOJ3958】[WF2011]Mummy Madness 二分+扫描线+线段树

    [BZOJ3958][WF2011]Mummy Madness Description 在2011年ACM-ICPC World Finals上的一次游览中,你碰到了一个埃及古墓. 不幸的是,你打开了 ...

  6. HDU 3265/POJ 3832 Posters(扫描线+线段树)(2009 Asia Ningbo Regional)

    Description Ted has a new house with a huge window. In this big summer, Ted decides to decorate the ...

  7. 【bzoj4491】我也不知道题目名字是什么 离线扫描线+线段树

    题目描述 给定一个序列A[i],每次询问l,r,求[l,r]内最长子串,使得该子串为不上升子串或不下降子串 输入 第一行n,表示A数组有多少元素接下来一行为n个整数A[i]接下来一个整数Q,表示询问数 ...

  8. hdu1542 Atlantis(扫描线+线段树+离散)矩形相交面积

    题目链接:点击打开链接 题目描写叙述:给定一些矩形,求这些矩形的总面积.假设有重叠.仅仅算一次 解题思路:扫描线+线段树+离散(代码从上往下扫描) 代码: #include<cstdio> ...

  9. [Codeforces 266E]More Queries to Array...(线段树+二项式定理)

    [Codeforces 266E]More Queries to Array...(线段树+二项式定理) 题面 维护一个长度为\(n\)的序列\(a\),\(m\)个操作 区间赋值为\(x\) 查询\ ...

随机推荐

  1. 3.2 Dependencies of the Projects in the Solution 解决方案中项目间的依赖项

    3.2 Dependencies of the Projects in the Solution 解决方案中项目间的依赖项 The diagram below shows the essential ...

  2. 使用寄存器点亮LED

    1. 项目:使用stm32寄存器点亮LED, 分别点亮红.绿.蓝3个灯. 2. 代码: 只需要编写main.c程序,stm3210x.h程序为空(只需要新建即可). 2.1 点亮绿灯main.c程序 ...

  3. VMware虚拟机安装Linux

    我们都知道,Linux的学习如果依靠大量的物理真机,是不切实际的,会非常的麻烦. 今天来和分享一下VMware虚拟机安装Linux操作系统的方法 (centos  7) 1. 我们要先把VMware虚 ...

  4. BUAA 2020 软件工程 个人博客作业

    BUAA 2020 软件工程 个人博客作业 Author: 17373051 郭骏 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 个人博客作业 ...

  5. 并发编程从零开始(十二)-Lock与Condition

    并发编程从零开始(十二)-Lock与Condition 8 Lock与Condition 8.1 互斥锁 8.1.1 锁的可重入性 "可重入锁"是指当一个线程调用 object.l ...

  6. Atcoder 题目泛做

    我思维越来越菜了,这样下去感觉要退役了. 听说Atcoder的题练思维?那就试着做一做吧. 坚持每天一两道吧.(很有可能咕掉.) AGC036 官方题解 ---A-Triangle          ...

  7. hdu 1506 Largest Rectangle in a Histogram(DP)

    题意: 有一个柱状图,有N条柱子.每一条柱子宽度都为1,长度为h1...hN. 在这N条柱子所构成的区域中找到一个最大面积,每平方米3块钱,问最多赚多少钱. 输入: 1<=N<=10000 ...

  8. QuantumTunnel:协议路由 vs 端口路由

    本篇来聊一下内网穿透中流量转发的问题 内网穿透和核心逻辑是根据流量的路由信息准确地将公网流量路由到指定的机器端口上,从而完成一次流量的内网穿透. 这里有一个核心问题,路由信息从哪里获取? 常见的有将路 ...

  9. ansible模块及语法

    常用模块详解 模块说明及示例: 1.ping模块ping模块 主要用于判断远程客户端是否在线,用于ping本身服务器,返回值是changed.ping示例 ansible clu -m ping 2. ...

  10. 【.NET 与树莓派】用 MPD 制作数字音乐播放器

    树莓派的日常家居玩法多多,制作一台属于自己的数字音乐播放机是其中的一种.严格上说,树莓派是没有声卡的,其板载的 3.5 mm 音频孔实际是通过 PWM 来实现音频输出的(通过算法让PWM信号变成模拟信 ...