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

题面

分析

首先把一组询问(x,y)看成二维平面上的一个点,我们想办法用数据结构维护这个二维平面(注意根据题意这里的y要-1,这样问题变成[x,y]区间是否是由连续的一段1组成)

如果我们改变第x个灯的状态,那么只有原来满足全1的条件,且现在不满足全1条件的区间受到影响。设包含x的最大的连续1的区间为[l,r],则左端点在[l,x],右端点在[x,r]的询问会受到影响。转化到二维平面上,就变成x坐标在[l,x]之内,y坐标在[x,r]之间的点会受到影响。即左下角为(l,x),右上角为(x,r)的矩形内的点

然后考虑每次修改对答案的影响。假如一个灯在t1时刻被点亮,t2时刻被熄灭,则它对答案的贡献为\(t_2-t_1\)。因此,如果时间为i时的修改使得第x个灯被点亮,我们就把左下角为(l,x),右上角为(x,r)的矩形内的点的值-i。如果时间为i时的修改使得第x个灯被熄灭,我们就把左下角为(l,x),右上角为(x,r)的矩形内的点的值+i

对于时间为i的询问(x,y),我们查询点(x,y)的值v,如果当前不能从x到y,那就直接输出v.否则,就输出i+v.因为只有某个灯熄灭的时候我们才更新它的贡献,如果询问时没熄灭,就要加上这些贡献。

这样,我们就把问题转化成二维的区间修改,单点查询问题,可以用标记永久化的二维线段树解决,但是代码实现难度较大。我们可以差分,把问题转化为二维的单点修改,区间查询问题

如果把左下角为 \((x_a,y_a)\),右上角\((x_b,y_b)\)的闭区间都加一个 v,那么其实就是:\((x_a,y_a)\)\(加v,\)

\((x_a,y_b+1)\)减 v,\((x_b+1,y_a)\)减 v,\((x_b+1,y_b+1)\)加 v.查询\((x,y)\)就查询(1,1)到\((x,y)\)的矩形内的点的权值和

这是一个经典的cdq分治问题,方法类似:[BZOJ 2683] 简单题 (CDQ分治)

也可以用树状数组维护第一维,动态开点的线段树维护第二维在线求解

时间复杂度均为\(O(n\log^2 n)\)

找全1的区间用set维护0的位置,二分查找出左边的第一个0和右边的第一个0的位置即可

代码

cdq分治:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#define maxn 300000
using namespace std;
typedef long long ll;
int n,m;
int a[maxn+5];
set<int>S;//存储0的位置 int tot;
struct node{
int a;
int b;
int c;
int type;
int val;
int id;
ll ans;
node(){ }
node(int _a,int _b,int _c,int _type,int _val){
a=_a;
b=_b;
c=_c;
type=_type;
val=id=ans=0;
if(_type==2) id=_val;
else val=_val;
}
void print(){
if(type==1){
printf("update tim=%d (%d,%d) val=%d\n",a,b,c,val);
}else{
printf("query tim=%d (%d,%d) id=%d\n",a,b,c,id);
}
}
}q[maxn*4+5];
int cmpa(node p,node q){
if(p.a==q.a){
if(p.b==q.b) return p.c<q.c;
else return p.b<q.b;
}else return p.a<q.a;
}
int cmpb(node p,node q){
if(p.b==q.b) return p.c<q.c;
else return p.b<q.b;
} struct fenwick_tree{
ll c[maxn+5];
inline int lowbit(int x){
return x&(-x);
}
inline void update(int pos,int val){
for(int i=pos;i<=maxn;i+=lowbit(i)) c[i]+=val;
}
inline ll query(int pos){
int ans=0;
for(int i=pos;i>0;i-=lowbit(i)) ans+=c[i];
return ans;
}
}T;
node tmp[maxn*4+5];
void cdq_divide(ll l,ll r){
int mid=(l+r)>>1;
if(l==r) return;
cdq_divide(l,mid);
cdq_divide(mid+1,r);
int ptr=l-1;
for(int i=mid+1;i<=r;i++){
while(ptr<mid&&q[ptr+1].b<=q[i].b){
ptr++;
if(q[ptr].type==1) T.update(q[ptr].c,q[ptr].val);
}
if(q[i].type==2) q[i].ans+=T.query(q[i].c);
}
for(int i=l;i<=ptr;i++) if(q[i].type==1) T.update(q[i].c,-q[i].val);
int pl=l,pr=mid+1;
int num=l-1;
while(pl<=mid&&pr<=r){
if(cmpb(q[pl],q[pr])) tmp[++num]=q[pl++];
else tmp[++num]=q[pr++];
}
while(pl<=mid) tmp[++num]=q[pl++];
while(pr<=r) tmp[++num]=q[pr++];
for(int i=l;i<=r;i++) q[i]=tmp[i];
} ll ans[maxn+5];
int main(){
char cmd[10];
int l,r;
int x;
int cntq=0;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++) scanf("%1d",&a[i]);
for(int i=1;i<=n;i++){
if(a[i]==0) S.insert(i);
}
S.insert(0);
S.insert(n+1);
for(int i=1;i<=m;i++){
scanf("%s",cmd);
if(cmd[0]=='q'){
cntq++;
scanf("%d %d",&l,&r);
r--;
q[++tot]=node(i,l,r,2,cntq);
set<int>::iterator it1=S.upper_bound(l);
if(*it1>r&&a[l]==1&&a[r]==1){
ans[cntq]+=i;
// printf("(%d,%d) + %d\n",l,r,i);
}
}else{
scanf("%d",&x);
a[x]^=1;
if(a[x]==1){//亮起
set<int>::iterator pre=S.lower_bound(x);
pre--;
set<int>::iterator nex=S.upper_bound(x);
l=*pre+1;
r=*nex-1;
// printf("for x in [%d,%d], y in [%d,%d],+%d\n",l,x,x,r,-i);
q[++tot]=node(i,l,x,1,-i);
q[++tot]=node(i,l,r+1,1,i);
q[++tot]=node(i,x+1,x,1,i);
q[++tot]=node(i,x+1,r+1,1,-i);
S.erase(x);
}else{//熄灭
set<int>::iterator pre=S.lower_bound(x);
pre--;
set<int>::iterator nex=S.upper_bound(x);
l=*pre+1;
r=*nex-1;
// printf("for x in [%d,%d], y in [%d,%d],+%d\n",l,x,x,r,i);
q[++tot]=node(i,l,x,1,i);
q[++tot]=node(i,l,r+1,1,-i);
q[++tot]=node(i,x+1,x,1,-i);
q[++tot]=node(i,x+1,r+1,1,i);
S.insert(x);
}
}
}
// for(int i=1;i<=tot;i++) q[i].print();
sort(q+1,q+1+tot,cmpa);
cdq_divide(1,tot);
for(int i=1;i<=tot;i++){
if(q[i].type==2) ans[q[i].id]+=q[i].ans;
}
for(int i=1;i<=cntq;i++) printf("%lld\n",ans[i]);
}

树套树:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#define maxn 300000
#define maxlogn 50
using namespace std;
typedef long long ll;
int n,m;
int a[maxn+5];
set<int>S;//存储0的位置 struct dynamic_segment_tree{
#define lson(x) tree[x].ls
#define rson(x) tree[x].rs
struct node{
int ls;
int rs;
ll val;
}tree[maxn*maxlogn+5];
int ptr=0;
void push_up(int x){
tree[x].val=tree[lson(x)].val+tree[rson(x)].val;
}
void update(int &x,int upos,ll uval,int l,int r){
if(!x) x=++ptr;
if(l==r){
tree[x].val+=uval;
return;
}
int mid=(l+r)>>1;
if(upos<=mid) update(tree[x].ls,upos,uval,l,mid);
else update(tree[x].rs,upos,uval,mid+1,r);
push_up(x);
}
ll query(int x,int L,int R,int l,int r){
if(x==0) return 0;
if(L<=l&&R>=r) return tree[x].val;
int mid=(l+r)>>1;
ll ans=0;
if(L<=mid) ans+=query(tree[x].ls,L,R,l,mid);
if(R>mid) ans+=query(tree[x].rs,L,R,mid+1,r);
return ans;
}
}T1;
struct fenwick_tree{
int root[maxn+5];
inline int lowbit(int x){
return x&(-x);
}
inline void update(int x,int y,ll val){
// printf("(%d,%d) + %d\n",x,y,val);
for(int i=x;i<=n+1;i+=lowbit(i)){
T1.update(root[i],y,val,1,n+1);
}
}
inline ll query(int x,int y){
ll ans=0;
for(int i=x;i>0;i-=lowbit(i)){
ans+=T1.query(root[i],1,y,1,n+1);
}
return ans;
}
}T2; ll ans[maxn+5];
int main(){
char cmd[10];
int l,r;
int x;
int cntq=0;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++) scanf("%1d",&a[i]);
for(int i=1;i<=n;i++){
if(a[i]==0) S.insert(i);
}
S.insert(0);
S.insert(n+1);
for(int i=1;i<=m;i++){
scanf("%s",cmd);
if(cmd[0]=='q'){
cntq++;
scanf("%d %d",&l,&r);
r--;
set<int>::iterator it1=S.upper_bound(l);
if(*it1>r&&a[l]==1&&a[r]==1){
printf("%lld\n",T2.query(l,r)+i);
}else{
printf("%lld\n",T2.query(l,r));
}
}else{
scanf("%d",&x);
a[x]^=1;
if(a[x]==1){//亮起
set<int>::iterator pre=S.lower_bound(x);
pre--;
set<int>::iterator nex=S.upper_bound(x);
l=*pre+1;
r=*nex-1;
// printf("for x in [%d,%d], y in [%d,%d],+%d\n",l,x,x,r,-i);
T2.update(l,x,-i);
T2.update(l,r+1,i);
T2.update(x+1,x,i);
T2.update(x+1,r+1,-i);
S.erase(x);
}else{//熄灭
set<int>::iterator pre=S.lower_bound(x);
pre--;
set<int>::iterator nex=S.upper_bound(x);
l=*pre+1;
r=*nex-1;
T2.update(l,x,i);
T2.update(l,r+1,-i);
T2.update(x+1,x,-i);
T2.update(x+1,r+1,i);
// printf("for x in [%d,%d], y in [%d,%d],+%d\n",l,x,x,r,i);
S.insert(x);
}
}
}
}
/*
5 2
11011
toggle 3
query 3 4
*/

[APIO2019] [LOJ 3146] 路灯 (cdq分治或树状数组套线段树)的更多相关文章

  1. BZOJ.4553.[HEOI2016&TJOI2016]序列(DP 树状数组套线段树/二维线段树(MLE) 动态开点)

    题目链接:BZOJ 洛谷 \(O(n^2)\)DP很好写,对于当前的i从之前满足条件的j中选一个最大值,\(dp[i]=d[j]+1\) for(int j=1; j<i; ++j) if(a[ ...

  2. bzoj 3110: [Zjoi2013]K大数查询 树状数组套线段树

    3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1384  Solved: 629[Submit][Stat ...

  3. [BZOJ 3196] 213平衡树 【线段树套set + 树状数组套线段树】

    题目链接:BZOJ - 3196 题目分析 区间Kth和区间Rank用树状数组套线段树实现,区间前驱后继用线段树套set实现. 为了节省空间,需要离线,先离散化,这样需要的数组大小可以小一些,可以卡过 ...

  4. [BZOJ 1901] Dynamic Rankings 【树状数组套线段树 || 线段树套线段树】

    题目链接:BZOJ - 1901 题目分析 树状数组套线段树或线段树套线段树都可以解决这道题. 第一层是区间,第二层是权值. 空间复杂度和时间复杂度均为 O(n log^2 n). 线段树比树状数组麻 ...

  5. P3157 [CQOI2011]动态逆序对(树状数组套线段树)

    P3157 [CQOI2011]动态逆序对 树状数组套线段树 静态逆序对咋做?树状数组(别管归并QWQ) 然鹅动态的咋做? 我们考虑每次删除一个元素. 减去的就是与这个元素有关的逆序对数,介个可以预处 ...

  6. BZOJ 1901 Zju2112 Dynamic Rankings 树状数组套线段树

    题意概述:带修改求区间第k大. 分析: 我们知道不带修改的时候直接上主席树就可以了对吧?两个版本号里面的节点一起走在线段树上二分,复杂度是O((N+M)logN). 然而这里可以修改,主席树显然是凉了 ...

  7. bzoj3196 二逼平衡树 树状数组套线段树

    题目传送门 思路:树状数组套线段树模板题. 什么是树状数组套线段树,普通的树状数组每个点都是一个权值,而这里的树状数组每个点都是一颗权值线段树,我们用前缀差分的方法求得每个区间的各种信息, 其实关键就 ...

  8. 【序列操作IV】树状数组套线段树/树套树

    题目描述 给出序列 a1,a2,…,an(0≤ai≤109),有关序列的两种操作. 1. ai(1≤i≤n)变成 x(0≤x≤109). 2. 求 al,al+1,…,ar(1≤l≤r≤n)第 k(1 ...

  9. 2019南昌网络赛  I. Yukino With Subinterval 树状数组套线段树

    I. Yukino With Subinterval 题目链接: Problem Descripe Yukino has an array \(a_1, a_2 \cdots a_n\). As a ...

随机推荐

  1. 【leetcode】1129. Shortest Path with Alternating Colors

    题目如下: Consider a directed graph, with nodes labelled 0, 1, ..., n-1.  In this graph, each edge is ei ...

  2. mobx状态管理快速入门

    1.mobx状态管理   安装: creact-react-app mobx  

  3. POJ 3691 DNA repair ( Trie图 && DP )

    题意 : 给出 n 个病毒串,最后再给出一个主串,问你最少改变主串中的多少个单词才能使得主串中不包含任何一个病毒串 分析 : 做多了AC自动机的题,就会发现这些题有些都是很套路的题目.在构建 Trie ...

  4. 【bzoj3566】 [SHOI2014]概率充电器

    *题目描述: 著名的电子产品品牌 SHOI 刚刚发布了引领世界潮流的下一代电子产品——概率充电器: “采用全新纳米级加工技术,实现元件与导线能否通电完全由真随机数决定!SHOI 概率充电器,您生活不可 ...

  5. Codeforces Round #603 (Div. 2) E. Editor

    E. Editor 题目链接: https://codeforces.com/contest/1263/problem/E 题目大意: 输入一个字符串S1含有‘(’ , ‘)’ , ‘R’ , ‘L’ ...

  6. 如何降低Vue.js项目中Webpack打包文件的大小?

    https://blog.csdn.net/maray/article/details/50988500?utm_source=blogxgwz0 import Blur from ‘vux/src/ ...

  7. 我用HTML写简历

    本文属于原创文章,转载请注明--来自桃源小盼的博客 起因 每次换工作写简历都是有点痛苦的事情,尤其是下载word模板,各种注册流程,有的还得买积分,冲会员,甚是不爽.就算下载好了,修改其中的一些细节也 ...

  8. 解决VS中不能搜索被折叠的代码块

    问题不大,但容易被忽略,解决方法:

  9. 微信小程序 form 组件

    表单组件:将组件内用户输入的 <switch> <input> <checkbox> <slider> <radio> <picker ...

  10. dp基础大概 (8.6)

    一些前言: 据说动态规划会用排序,数据结构来进行乱搞优化操作 动态规划滴核心是个啥呢?状态表示和状态转移 设状态:哪些因素会影响到最终答案,就把哪些因素用数组的维度表示出来 要充分描述,也要简洁 举个 ...