洛谷 P5445 - 路燈


洛谷題目頁面傳送門

題意見洛谷。

首先考慮實時維護\(\forall i,j\in[1,n]\),目前有多少時刻滿足能從\(i\)到達\(j\)

顯然,\(i\)能到達\(j\)當且僅當\(i,j\)在同一個亮燈連續段里。將當前狀態剖成若干個極大亮燈連續段,那么能否到達關於\(i,j\)兩維的函數應該是由這些連續段,每段分別在\(i\)軸和\(j\)軸上作為兩條鄰邊、\(j=i\)上的一段作為副對角線的一些充滿\(1\)的正方形組成的,其他地方都是\(0\)(別問為啥不貼圖,問就是我不是良心博主)

那么在某一時刻的答案函數就是之前所有時刻的所對應的上述01函數之和。考慮實時維護這個答案函數(二維函數,即一個矩陣)。

一種很容易想到的方法是遞推,每次將當前時刻的01函數加到當前維護的答案函數里面去。當前時刻的01函數顯然是由上一時刻的01函數進行常數次矩形修改得來的(開燈就是連接兩邊接壤的\(1\)矩形並補全,關燈就是從所在\(1\)矩形斷開,至於如何維護這些連續段,set即可,太簡單不多說),而將01函數加到答案函數里去又等價於對答案矩陣進行若干次(這里不是常數次了,而是連續段個數次,即\(1\)矩形個數次)矩形增加\(1\)。這里對01函數的更新顯然是力所能及的,而對答案函數的更新的復雜度就沒法保證了。

不難發現,這里對01函數的更新是若干次矩形加\(1\),其中\(1\)是個常數,而這些矩形隨時刻遞增又是常數差異的,就一臉可以優化成在這些常數級別的差異上增加非常數,從而保證復雜度。

考慮一個01函數關於時刻的三維函數。考慮當前答案函數的每一處在這個三維函數上的意義:顯然是從當前時刻斷開時間軸得到的縱切面的左邊的所有此處的和。這是一些\(0/1\)的和,\(0\)顯然不用考慮,剩下來就是一些\(1\)的區間,設為\([l_1,r_1],[l_2,r_2],\cdots,[l_k,r_k]\),那么這個和就是\(\sum\limits_{i=1}^k(r_i-l_i+1)\)。考慮在一個區間開始的時候給此處貢獻上\(-l_i\),在區間結束的時候給此處貢獻上\(r_i+1\)。此時增加量顯然不是常數了,我們來看看增加次數有沒有減少。區間開始和區間結束的時候,就是相鄰01函數差異對此處影響的時候,每次時刻的遞推都只有01函數差異的矩形量次矩形增加,哦吼,可以了。需要注意的是,若當前要查詢的那處在01函數中為\(1\)的話,那么第\(k\)個區間還未結束,我們要強行令它結束,即在答案函數在此處的值的基礎上再加上當前時刻。

(上面這一段重要的轉化是本題的瓶頸。個人感覺這個哲學思想理解的還不是很透徹,大概以后重點做DS的時候題做多了感覺就上來了吧。)

接下來就是個矩形增加、單點查詢的事了。看起來能夠線段樹套動態開點線段樹,但寫到一半才發現外層線段樹的懶標記無法\(\mathrm O(1)\)存儲。於是想到將修改和查詢范圍顛倒的方式:差分。考慮二維差分,這樣一次矩形增加轉化為\(4\)次單點增加,單點查詢轉化為前綴矩形求和。這就是個經典的二維數點模型,寫一發BIT套動態開點線段樹即可(萌新第一次寫這個,多多關照)。二維數點應該是有其他方法的(如cdq分治),然而我還沒有系統的學這一塊,不管,而且這題重點不在這里。

btw,這是我第二次寫vector動態開點數據結構(第一次是列隊),犯了跟列隊同樣的錯誤(UB+vector分配內存原理造成賦不進去值)。多錯幾次就不會犯了。

代碼:

#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define mp make_pair
#define X first
#define Y second
const int inf=0x3f3f3f3f;
int lowbit(int x){return x&-x;}
const int N=300000;
int n,qu; 
char a[N+5];
struct segtree{//動態開點線段樹 
	struct node{int lson,rson,l,r,sum;};
	#define lson(p) nd[p].lson
	#define rson(p) nd[p].rson
	#define l(p) nd[p].l
	#define r(p) nd[p].r
	#define sum(p) nd[p].sum
	vector<node> nd;
	int nwnd(int l=1,int r=n){return nd.pb(node({0,0,l,r,0})),nd.size()-1;}
	void init(){//初始化 
		nd.pb(node({0,0,0,0,0}));
		nwnd();
	}
	void add(int x,int v,int p=1){//單點增加 
		sum(p)+=v;
		if(l(p)==r(p))return;
		int mid=l(p)+r(p)>>1,res;
		if(x<=mid){
			if(!lson(p))res=nwnd(l(p),mid),lson(p)=res;
			add(x,v,lson(p));
		}
		else{
			if(!rson(p))res=nwnd(mid+1,r(p)),rson(p)=res;
			add(x,v,rson(p));
		}
	}
	int _sum(int l,int r,int p=1){//區間求和 
		if(!p)return 0; 
		if(l<=l(p)&&r>=r(p))return sum(p);
		int mid=l(p)+r(p)>>1,res=0;
		if(l<=mid)res+=_sum(l,r,lson(p));
		if(r>mid)res+=_sum(l,r,rson(p));
		return res;
	}
};
struct bitree{//BIT 
	segtree segt[N+1];
	void init(){//初始化 
		for(int i=1;i<=n;i++)segt[i].init();
	}
	void add(int x,int y,int v){
		if(y>n)return;
		while(x<=n)segt[x].add(y,v),x+=lowbit(x);
	}
	void add(int l1,int r1,int l2,int r2,int v){//矩形增加 
		add(r1+1,r2+1,v);add(l1,r2+1,-v);add(r1+1,l2,-v);add(l1,l2,v);//轉化為差分數組上的單點增加 
	}
	int val(int x,int y){//單點查詢 
		int res=0;
		while(x)res+=segt[x]._sum(1,y),x-=lowbit(x);//轉化為差分數組上的矩形求和 
		return res;
	}
}bit;
int main(){
	cin>>n>>qu;
	scanf("%s",a+1);
	set<pair<int,int> > st;
	for(int i=1,las=0;i<=n+1;i++)
		if(a[i]=='1')las=las?las:i;
		else if(las)st.insert(mp(las,i-1)),las=0;
	bit.init();
	for(int i=1;i<=qu;i++){
		char tp[10];int x,y;
		scanf(" %s%d",tp,&x);
		if(tp[0]=='t'){
			set<pair<int,int> >::iterator fd=st.upper_bound(mp(x,inf));
			if(fd!=st.begin()&&x<=(--fd)->Y){//關燈 
				int l=fd->X,r=fd->Y;
				st.erase(fd);
				bit.add(l,r,l,r,i);
				if(l<x)st.insert(mp(l,x-1)),bit.add(l,x-1,l,x-1,-i);
				if(r>x)st.insert(mp(x+1,r)),bit.add(x+1,r,x+1,r,-i);
			}
			else{//開燈 
				fd=st.upper_bound(mp(x,inf));
				int l=x,r=x;
				if(fd!=st.begin())fd--,fd->Y==x-1?bit.add(fd->X,fd->Y,fd->X,fd->Y,i),l=fd->X,st.erase(fd++):fd++;
				if(fd!=st.end())fd->X==x+1&&(bit.add(fd->X,fd->Y,fd->X,fd->Y,i),r=fd->Y,st.erase(fd),0);
				st.insert(mp(l,r));
				bit.add(l,r,l,r,-i);
			}
		}
		else{
			scanf("%d",&y);y--;
			set<pair<int,int> >::iterator fd=st.upper_bound(mp(x,inf));
			if(fd!=st.begin()&&y<=(--fd)->Y)printf("%d\n",bit.val(x,y)+i);
			else printf("%d\n",bit.val(x,y));
		}
	}
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM