【洛谷P5445】路灯

题目

题目链接: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\) 时刻,每时刻会发生下列两种事件之一:

  • \(\text{toggle} \ i\):切换第 \(i\) 个路灯的状态。具体地说,若路灯原来亮起,则现在将熄灭;若路灯原来熄灭,则现在将亮起。
  • \(\text{query} \ a \ b\):出租车部门的负责人想知道,从 \(0\) 时刻起到当前时刻,有多少个时刻满足:出租车能够从站点 \(a\) 出发到达站点 \(b\)

请你帮助出租车部门的负责人回答他们的问题。
\(n,q\leq 3\times 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(n\log^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\n",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;
}
posted @ 2021-02-21 11:36  stoorz  阅读(31)  评论(0编辑  收藏  举报