题目
题目链接:https://www.luogu.com.cn/problem/P5445
一辆自动驾驶的出租车正在 Innopolis 的街道上行驶。该街道上有 (n+1) 个停车站点,它们将街道划分成了 (n) 条路段。每一路段都拥有一个路灯。当第 (i) 个路灯亮起,它将照亮连接第 (i) 与第 (i+1) 个站点的路段。否则这条路段将是黑暗的。
安全起见,出租车只能在被照亮的路段上行驶。换言之,出租车能从站点 (a) 出发到达站点 (b (a<b)) 的条件是:连接站点 (a) 与 (a+1),(a+2) 与 ,……,(b-1) 与 (b) 的路段都被照亮。
在经过一些意外故障或修理之后,街道上的路灯可能是亮起的,也可能是熄灭的。
现在给定 (0) 时刻时,街道上路灯的初始状态。之后 (1,2,ldots,q) 时刻,每时刻会发生下列两种事件之一:
- ( ext{toggle} i):切换第 (i) 个路灯的状态。具体地说,若路灯原来亮起,则现在将熄灭;若路灯原来熄灭,则现在将亮起。
- ( ext{query} a b):出租车部门的负责人想知道,从 (0) 时刻起到当前时刻,有多少个时刻满足:出租车能够从站点 (a) 出发到达站点 (b)。
请你帮助出租车部门的负责人回答他们的问题。
(n,qleq 3 imes 10^3)。
思路
假设我们把第 (i) 个路灯点亮了,可以用一个 set 维护暗的灯,那么在 set 上可以找到 (i) 左右两边第一个暗的灯,那么自然就得到了可以通行的区间 ([l,r])。这一次操作把 ([l,i]) 与 ((i,r]) 之间的路打通了。
考虑映射到二维平面上,也就是把横坐标范围 ([l,i]),纵坐标范围 ((i,r]) 的矩形全部加上 (m-t),其中 (m) 是总时间,(t) 是当前时间。变暗同理,直接把矩形减去 (m-t)。这样如果我们查询 (a,b),只需要求 ((a,b)) 处的值就可以了。注意如果此时 (a,b) 还可以互相到达,答案需要减去 (m-t)。
那么现在的问题就是一个矩形加,单点查询的问题。可以用树状数组套线段树解决。我们把矩形加变为 (4) 次单点修改,在对应的 (log) 棵线段树上单点修改,然后查询的时候查询线段树前缀和即可。
时空复杂度都是 (O(nlog^2 n))。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=300010,LG=20,MAXN=30000000;
int n,m,a[N];
char ch[10];
set<int> s;
struct SegTree
{
int tot,lc[MAXN],rc[MAXN];
ll sum[MAXN];
void pushup(int x)
{
sum[x]=sum[lc[x]]+sum[rc[x]];
}
int update(int x,int l,int r,int k,int v)
{
if (!x) x=++tot;
if (l==r)
{
sum[x]+=v;
return x;
}
int mid=(l+r)>>1;
if (k<=mid) lc[x]=update(lc[x],l,mid,k,v);
else rc[x]=update(rc[x],mid+1,r,k,v);
pushup(x);
return x;
}
int query(int x,int l,int r,int ql,int qr)
{
if (!x) return 0;
if (ql<=l && qr>=r) return sum[x];
int mid=(l+r)>>1,ans=0;
if (ql<=mid) ans+=query(lc[x],l,mid,ql,qr);
if (qr>mid) ans+=query(rc[x],mid+1,r,ql,qr);
return ans;
}
}seg;
struct BIT
{
int rt[N];
void add(int x,int k,int v)
{
if (!x || k>n) return;
for (int i=x;i<=n;i+=i&-i)
rt[i]=seg.update(rt[i],1,n,k,v);
}
ll query(int x,int y)
{
ll ans=0;
for (int i=x;i;i-=i&-i)
ans=ans+seg.query(rt[i],1,n,1,y);
return ans;
}
}bit;
void update(int t,int i)
{
int l=(*(--s.lower_bound(i)))+1;
int r=(*s.upper_bound(i))-1;
a[i]^=1;
if (a[i])
{
s.erase(s.find(i));
bit.add(l,i+1,m-t);
bit.add(l,r+2,-m+t);
bit.add(i+1,i+1,-m+t); bit.add(i+1,r+2,m-t);
}
else
{
s.insert(i);
bit.add(l,i+1,-m+t); bit.add(l,r+2,m-t);
bit.add(i+1,i+1,m-t); bit.add(i+1,r+2,-m+t);
}
}
void query(int t,int x,int y)
{
ll ans=bit.query(x,y);
if (*s.lower_bound(x)>=y) ans-=m-t;
printf("%lld
",ans);
}
int main()
{
scanf("%d%d",&n,&m);
n++;
for (int i=0;i<=n;i++)
s.insert(i);
for (int i=1,x;i<n;i++)
{
scanf("%1d",&x);
if (x) update(0,i);
}
for (int i=1;i<=m;i++)
{
scanf("%s",ch);
if (ch[0]=='t')
{
int x;
scanf("%d",&x);
update(i,x);
}
else
{
int x,y;
scanf("%d%d",&x,&y);
query(i,x,y);
}
}
return 0;
}