題解 P4979 【礦洞:坍塌】

CYJian家的礦塌了之後,就沒有經濟來源了(不要問我怎麼沒有存款)。

於是,CYJian迫切地想要修復他家的礦。

CYJian家的礦共出產A,B,CA,B,C三種礦石,所以我們也只能用A,B,CA,B,C三種材料來修復他們家的礦。我們已知共有NN噸材料,每噸材料均爲A,B,CA,B,C三種材料中的一種,它們連成了一個串,如:
ABCBCABCBACBCBAAABCBCABCBACBCBAA
CYJian家對材料的要求非常嚴格,他每次會選擇一段連續區間的材料作爲修復的材料。因爲不合要求的材料會使得礦再次塌陷,砸死CYJian,所以這個連續區間的材料必須滿足一下22個要求:

  • 這段連續區間必須是同一種材料

  • 這段連續區間的前一個材料與後一個材料必須不相同。

例如,有一段材料爲AACBBABBBCCCBBBAACBBABBBCCCBBB,則(4(4~5)5) 區間的 BBBB(5(5~5)5) 區間的 BB 均符合要求,而 (10(10~12)12) 區間的 CCCCCC 不符合要求。

材料有靈性,所以材料會有變化。

現在有NN噸材料,KK個詢問。每個詢問是以下的22種形式之一:

  • A x y op 表示替換材料,將 xxy(1xyN)y(1\leq x\leq y\leq N) 區間內的材料替換爲 opopopopA,B,CA,B,C三種材料字符中的一個。

  • B x y 表示是否詢問,即詢問xxy(1xyN)y(1\leq x\leq y\leq N)區間內的材料是否合法,合法輸出YesYes,不合法輸出NoNo

注意:當x=1x=1y=Ny=N時,你的程序不需要判斷前後的情況,而只需要判斷區間內的情況.

礦洞:坍塌 題解

分析

首先,我們考慮使用線段樹。

我們發現

  • 對於操作 11

    • lazy_tag 修改就好了
  • 對於操作 22

    • 看這一段是否所有字符都相同
    • 如果 l1&rnl\ne1 \And r\ne n ,單點查詢第 l1l-1r+1r+122 個字符是否相同就行了

如何看一段內所有字符是否相同

這個東西確實有點難,我的做法是,雖然字符很多,但每個字符都是 ABC 中的一個,所以可以直接看這段字符是否全是 ABC

開始線段樹

維護

綜上所述,我們可以開始寫題了。

我們需要維護幾個值:

  • lazy 這個應該不用我說了吧,有 33 個值

    • 11 表示 A
    • 22 表示 B
    • 33 表示 C
  • s[3] 這個就是看這段字符是否全是 ABC

    • s1s_1 表示這段字符是否全是 A
    • s2s_2 表示這段字符是否全是 B
    • s3s_3 表示這段字符是否全是 C

pushup

這一段區間是否全是 ABC,僅當左兒子和右兒子全是 ABC

for(register int i=1;i<=3;i++)t[num].s[i]=(t[ls].s[i]&&t[rs].s[i]);

邊界條件

對於 l=rl=r 的情況,sis_i 的值是否爲 11,僅當這個字符是否是ABC

pushup

修改左子樹和右子樹

void pushdown(int num){
    if(t[num].lazy==1)down1(ls),down1(rs);
    if(t[num].lazy==2)down2(ls),down2(rs);
    if(t[num].lazy==3)down3(ls),down3(rs);
    t[num].lazy=0;
}

說說 downxdown_x 怎麼寫

首先把 lazy_tag 的值賦一下,然後,將 sxs_x 設爲 11,其他都設爲 00

void down1(int num){
    t[num].lazy=1;
    t[num].s[1]=1;
    t[num].s[2]=0;
    t[num].s[3]=0;
}
void down2(int num){
    t[num].lazy=2;
    t[num].s[1]=0;
    t[num].s[2]=1;
    t[num].s[3]=0;
}
void down3(int num){
    t[num].lazy=3;
    t[num].s[1]=0;
    t[num].s[2]=0;
    t[num].s[3]=1;
}

修改

這個是板子誒,不講了

void change(int num){
    if(L<=t[num].l&&t[num].r<=R){
        if(S==1)down1(num);
        if(S==2)down2(num);
        if(S==3)down3(num);
        return;
    }pushdown(num);
    if(t[ls].r>=L)change(ls);
    if(t[rs].l<=R)change(rs);
    pushup(num);
}

單點查詢

這個是板子誒,不講了

int query2(int num,int S){//S是要查詢的點
    if(t[num].l==t[num].r){
        for(int i=1;i<=3;i++)
            if(t[num].s[i])return i;
    }pushdown(num);
    if(t[ls].r>=S)return query2(ls,S);
    if(t[rs].l<=S)return query2(rs,S);
}

區間查詢

這個是板子誒,不講了

bool query1(int num,int f){
    if(L<=t[num].l&&t[num].r<=R)return t[num].s[f];
    pushdown(num);
    if(t[ls].r<L)return query1(rs,f);
    if(t[rs].l>R)return query1(ls,f);
    return (query1(ls,f)&&query1(rs,f));
}

代碼

// Problem : P4979 礦洞:坍塌
// Contest : Luogu
// URL : https://www.luogu.com.cn/problem/P4979
// Memory Limit : 250 MB
// Time Limit : 1000 ms
// Powered by CP Editor (https://github.com/cpeditor/cpeditor)

#include <bits/stdc++.h>
#define ls num<<1
#define rs num<<1|1
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
	T RR=1;FF=0;char CH=getchar();
	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
	FF*=RR;
}
const int MAXN=5e5+10;
int a[MAXN],L,R,S,n;
struct Line_Tree{
	struct Tree{
		int l,r,lazy;
		bool s[3];
	}t[MAXN<<2];
	void pushup(int num){
		for(int i=1;i<=3;i++)t[num].s[i]=(t[ls].s[i]&&t[rs].s[i]);
	}
	void down1(int num){
		t[num].lazy=1;
		t[num].s[1]=1;
		t[num].s[2]=0;
		t[num].s[3]=0;
	}
	void down2(int num){
		t[num].lazy=2;
		t[num].s[1]=0;
		t[num].s[2]=1;
		t[num].s[3]=0;
	}
	void down3(int num){
		t[num].lazy=3;
		t[num].s[1]=0;
		t[num].s[2]=0;
		t[num].s[3]=1;
	}
	void pushdown(int num){
		if(t[num].lazy==1)down1(ls),down1(rs);
		if(t[num].lazy==2)down2(ls),down2(rs);
		if(t[num].lazy==3)down3(ls),down3(rs);
		t[num].lazy=0;
	}
	void build(int num,int l,int r){
		t[num].l=l;t[num].r=r;t[num].lazy=0;
		if(l==r){
			for(int i=1;i<=3;i++)t[num].s[i]=(a[l]==i);
			return;
		}int mid=(l+r)>>1;
		build(ls,l,mid);
		build(rs,mid+1,r);
		pushup(num);
	}
	void change(int num){
		if(L<=t[num].l&&t[num].r<=R){
			if(S==1)down1(num);
			if(S==2)down2(num);
			if(S==3)down3(num);
			return;
		}pushdown(num);
		if(t[ls].r>=L)change(ls);
		if(t[rs].l<=R)change(rs);
		pushup(num);
	}
	bool query1(int num,int f){
		if(L<=t[num].l&&t[num].r<=R)return t[num].s[f];
		pushdown(num);
		if(t[ls].r<L)return query1(rs,f);
		if(t[rs].l>R)return query1(ls,f);
		return (query1(ls,f)&&query1(rs,f));
	}
	int query2(int num,int S){
		if(t[num].l==t[num].r){
			for(int i=1;i<=3;i++)
				if(t[num].s[i])return i;
		}pushdown(num);
		if(t[ls].r>=S)return query2(ls,S);
		if(t[rs].l<=S)return query2(rs,S);
	}
}T;
int init(){
	read(n);
	for(int i=1;i<=n;i++){
		char ch=getchar();
		for(;ch!='A'&&ch!='B'&&ch!='C';ch=getchar());
		a[i]=ch-64;
	}T.build(1,1,n);
	return 0;
}
int work2(){
	if(L==1||R==n){puts("Yes");return 0;}
	if(T.query2(1,L-1)!=T.query2(1,R+1))puts("Yes");
	else puts("No");
	return 0;
}
int work(){
	char ch=getchar();for(;ch!='A'&&ch!='B';ch=getchar());
	if(ch=='A'){
		read(L);read(R);ch=getchar();for(;ch!='A'&&ch!='B'&&ch!='C';ch=getchar());S=ch-64;
		T.change(1);
	}else{
		read(L);read(R);
		for(int i=1;i<=3;i++)
			if(T.query1(1,i)){work2();return 0;}
		puts("No");
	}
	return 0;
}
int main(){
	init();
	int T;read(T);
	while(T--){work();}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章