传送门·

对于询问 $(a,b)$ ,感觉一维很不好维护,考虑把询问看成平面上的一个点,坐标为 $(a,b)$

每个坐标 $(x,y)$ 的值表示到当前 $x$ 和 $y$ 联通的时间和

考虑一个修改的贡献,它其实就是把左边一段区间 $[l,x]$ 和右边一段区间 $[x+1,r]$ 联通或断开

放到平面上发现其实就是横坐标在 $[l,x]$ ,纵坐标在 $[x+1,r]$ 的矩形里修改,那么矩形左下角为 $[l,x+1]$,右上角为 $[x,r]$

如果每个时间点都把相应矩形 $+1$ 的话显然是不可行的,考虑起点和终点的时间差

如果当前操作是联通,设当前时间为 $t$,则把相应矩形 $-t$,如果是断开则把矩形加 $t$

这样我们询问时直接矩形单点查值即可,但是要注意,如果查询时当前区间仍然联通,那么对应矩形还没加 $t$ ,我们查完值以后答案还要再加 $t$

矩形加具体就是差分成四个前缀修改,查询的时候直接查左下所有位置的和即可

如果把左下角为 $(xa,ya)$,右上角 $(xb,yb)$ 的闭区间都加一个 $v$,那么其实就是把 $(0,0),(xa,ya)$ 加一个 $v$,$(0,0),(xa,yb+1)$ 减 $v$,$(0,0),(xb+1,ya)$ 减 $v$,$(0,0),(xb+1,yb+1)$ 加 $v$

二维平面加一维时间轴, $CDQ$ 分治即可

然后就是具体的维护了,我维护亮灯区间用的是闭区间,好像比较多细节...

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<set>
using namespace std;
typedef long long ll;
inline int read()
{
int x=,f=; char ch=getchar();
while(ch<''||ch>'') { if(ch=='-') f=-; ch=getchar(); }
while(ch>=''&&ch<='') { x=(x<<)+(x<<)+(ch^); ch=getchar(); }
return x*f;
}
const int N=6e5+;
char s[N];
int n,Q,tot;
struct dat{//区间
int l,r;
dat (int a=,int b=) { l=a,r=b; }
inline bool operator < (const dat &tmp) const {
return l<tmp.l;
}
};
set <dat> S;//维护开灯的区间
set <dat>::iterator ita,itb;
struct Poi{//CDQ分治的点
int x,y,id,v;//id=0表示是修改,v是修改的值
Poi (int a=,int b=,int c=,int d=) { x=a,y=b,id=c,v=d; }
}d[N<<],tmp[N<<];
int t[N];
inline void add(int x,int v) { while(x<=n+) t[x]+=v,x+=x&-x; }
inline int ask(int x) { int res=; while(x) res+=t[x],x-=x&-x; return res; }
inline bool fc(Poi &a,Poi &b) { if(a.x!=b.x) return a.x<b.x; return a.y!=b.y ? a.y<b.y : a.id<b.id; }
int ans[N];
void solve(int l,int r)
{
if(l==r) return; int mid=l+r>>;
solve(l,mid); solve(mid+,r);
int i=l,j=mid+,p=l-;
while(i<=mid&&j<=r)
{
if(fc(d[i],d[j]))
{
if(!d[i].id) add(d[i].y,d[i].v);
tmp[++p]=d[i++]; continue;
}
if(d[j].id) ans[d[j].id]+=ask(d[j].y);
tmp[++p]=d[j++];
}
while(i<=mid) { if(!d[i].id) add(d[i].y,d[i].v); tmp[++p]=d[i++]; }
while(j<=r) { if(d[j].id) ans[d[j].id]+=ask(d[j].y); tmp[++p]=d[j++]; }
for(int k=l;k<=mid;k++) if(!d[k].id) add(d[k].y,-d[k].v);//撤销
for(int k=l;k<=r;k++) d[k]=tmp[k];
}
int main()
{
n=read(),Q=read();
scanf("%s",s+);
S.insert(dat(,)); S.insert(dat(n+,n+));//初始虚区间防越界
for(int i=;i<=n;i++)
{
if(s[i]=='') continue;
int R=i; while(s[R+]=='') R++;
S.insert(dat(i,R)); i=R;//初始情况
}
char ch[]; int a,b,cnt=;
for(int i=;i<=Q;i++)
{
scanf("%s",ch); a=read();
if(ch[]=='q')
{
b=read();
d[++tot]=Poi(a,b,++cnt,);//询问点
ita=S.lower_bound(dat(a+,)); ita--;
itb=S.lower_bound(dat(b,)); itb--;
if( ita==itb && (*ita).r>=b- ) ans[cnt]+=i;//判断区间是否还联通
continue;
}
if(s[a]=='')//联通
{
ita=S.lower_bound(dat(a+,)); ita--; itb=ita; ita++;//找到左右区间
dat A=(*ita),B=(*itb); int l=a,r=a;
if(B.r==l-&&B.l>=) l=B.l,S.erase(B);//注意判断,注意边界
if(A.l==r+&&A.r<=n) r=A.r,S.erase(A);
S.insert(dat(l,r));
d[++tot]=Poi(l,a+,,-i); d[++tot]=Poi(a+,r+,,-i);
d[++tot]=Poi(l,r+,,i); d[++tot]=Poi(a+,a,,i);
//联通时矩形-i
s[a]=''; continue;
}
//断开
ita=S.lower_bound(dat(a+,)); ita--; dat A=(*ita);
S.erase(A); if(A.l<a) S.insert(dat(A.l,a-)); if(A.r>a) S.insert(dat(a+,A.r));//注意边界
d[++tot]=Poi(A.l,a+,,i); d[++tot]=Poi(a+,A.r+,,i);
d[++tot]=Poi(A.l,A.r+,,-i); d[++tot]=Poi(a+,a,,-i);
//断开时矩形+i
s[a]='';
}
solve(,tot);
for(int i=;i<=cnt;i++) printf("%d\n",ans[i]);
return ;
}

P5445 [APIO2019]路灯的更多相关文章

  1. P5445 [APIO2019]路灯(树套树)

    P5445 [APIO2019]路灯 转化为平面上的坐标(x,y),set维护连续区间. 用树套树维护矩阵加法,单点查询. 注意维护矩阵差分的时候, $(x,y,v)$是对$(x,y)(n+1,n+1 ...

  2. 【LOJ#3146】[APIO2019]路灯(树套树)

    [LOJ#3146][APIO2019]路灯(树套树) 题面 LOJ 题解 考场上因为\(\text{bridge}\)某个\(\text{subtask}\)没有判\(n=1\)的情况导致我卡了\( ...

  3. LOJ3146 APIO2019路灯(cdq分治+树状数组)

    每个时刻都形成若干段满足段内任意两点可达.将其视为若干正方形.则查询相当于求历史上某点被正方形包含的时刻数量.并且注意到每个时刻只有O(1)个正方形出现或消失,那么求出每个矩形的出现时间和消失时间,就 ...

  4. 题解-APIO2019路灯

    problem \(\mathtt {loj-3146}\) 题意概要:一条直线上有 \(n+1\) 个点和 \(n\) 条道路,每条道路连通相邻两个点.在 \(q\) 个时刻内,每个时刻有如下两种操 ...

  5. APIO2019简要题解

    Luogu P5444 [APIO2019]奇怪装置 看到这种题,我们肯定会想到\((x,y)\)一定有循环 我们要找到循环节的长度 推一下发现\(x\)的循环节长为\(\frac{AB}{B+1}\ ...

  6. 2021record

    2021-10-14 P2577 [ZJOI2004]午餐 2021-10-13 CF815C Karen and Supermarket(小小紫题,可笑可笑) P6748 『MdOI R3』Fall ...

  7. P3722 [AH2017/HNOI2017]影魔(单调栈+扫描线+线段树)

    题面传送门 首先我们把这两个贡献翻译成人话: 区间 \([l,r]\) 产生 \(p_1\) 的贡献当且仅当 \(a_l,a_r\) 分别为区间 \([l,r]\) 的最大值和次大值. 区间 \([l ...

  8. 【APIO2019】路灯(ODT & (树套树 | CDQ分治))

    Description 一条 \(n\) 条边,\(n+1\) 个点的链,边有黑有白.若结点 \(a\) 可以到达 \(b\),需要满足 \(a\to b\) 的路径上的边不能有黑的.现给出 \(0\ ...

  9. [APIO2019] [LOJ 3146] 路灯 (cdq分治或树状数组套线段树)

    [APIO2019] [LOJ 3146] 路灯 (cdq分治或树状数组套线段树) 题面 略 分析 首先把一组询问(x,y)看成二维平面上的一个点,我们想办法用数据结构维护这个二维平面(注意根据题意这 ...

随机推荐

  1. python装饰器的简单理解

    如果你接触 Python 有一段时间了的话,想必你对 @ 符号一定不陌生了,没错 @ 符号就是装饰器的语法糖. 装饰器的使用方法很固定: 先定义一个装饰函数(帽子)(也可以用类.偏函数实现) 再定义你 ...

  2. python 7行代码实现微信机器人自动回复

    首先 需要去 图灵 网站注册 获取api_key 先上代码 from wxpy import * bot = Bot() tuling = Tuling(api_key='你的api_key') @b ...

  3. LeetCode--142--环形链表II(python)

    给定一个链表,返回链表开始入环的第一个节点. 如果链表无环,则返回 null. 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始). 如果 pos 是 - ...

  4. JavaScript输出

    JavaScript不提供任何的内建或是打印方式 JavaScript的显示方案主要有以下四种: window.alert()  写入警告框 document.write()  写入 HTML 输出 ...

  5. SpringCloud 教程 (六)断路器聚合监控(Hystrix Turbine)

    一.Hystrix Turbine简介 看单个的Hystrix Dashboard的数据并没有什么多大的价值,要想看这个系统的Hystrix Dashboard数据就需要用到Hystrix Turbi ...

  6. 大数据笔记(十三)——常见的NoSQL数据库之HBase数据库(A)

    一.HBase的表结构和体系结构 1.HBase的表结构 把所有的数据存到一张表中.通过牺牲表空间,换取良好的性能. HBase的列以列族的形式存在.每一个列族包括若干列 2.HBase的体系结构 主 ...

  7. Microsoft windows terminal

    https://github.com/microsoft/terminal 尝试在windows store中安装,结果everything搜索不到 I tried running WindowsTe ...

  8. React-Native 之 GD (二十)removeClippedSubviews / modal放置的顺序 / Android 加载git图\动图 / 去除 Android 中输入框的下划线 / navigationBar

    1.removeClippedSubviews 用于提升大列表的滚动性能.需要给行容器添加样式overflow:’hidden’.(Android已默认添加此样式)此属性默认开启 这个属性是因为在早期 ...

  9. Splinter 的认识和基础应用

    Splinter 是一个使用Python开发的开源web应用测试程序,它可以帮助我们实现自动浏览站点和与其进行交互.它是依赖于其它python插件或拓展进行的,所以我们使用它之前需要安装一系列的依赖包 ...

  10. 【洛谷P1310 表达式的值】

    题目链接 题目描述 对于1 位二进制变量定义两种运算: 运算的优先级是: 先计算括号内的,再计算括号外的. “× ”运算优先于“⊕”运算,即计算表达式时,先计算× 运算,再计算⊕运算.例如:计算表达式 ...