abc223_F Parenthesis Checking 【线段树 | 匹配类问题思维方式】

ShanDong Multi-University Training #5 - Virtual Judge (csgrandeur.cn)

给定一个括号串。

操作1:交换第l个和第r个位置

操作2:判断是不是正确的括号序列。

关键思维:

对于匹配问题。

第一个:数量不匹配。第二个:对于任意一个),前面必须存在没有匹配的(。这个思想也完完全全是stack写括号匹配的思路!这是匹配问题的本质。

  •  要确定一件事,除了 “)(”这种类型的或者是“(”和“)”数量不匹配以外,其他的都是正确的。
  • 1、我们需要维护的信息是哪些呢?对于当前的序列你要保证不能出现第一种情况。记录对于当前序列的需要匹配的左括号为kl,又括号为kr。这个信息是可以传上来的。我们可以想象,当最后两个序列合并的时候,必须满足r.kl == l.kr && l.kr = r.kl = 0才算匹配成功。2、也可以将“)”视为-1,“(”视为1,和上面想法差不多,但有数字的妙用,用正负1来解决这种匹配的问题,再结合sum,差分等。根据上面的关键思维,我们就能得出,需要维护前缀和的最小值,最小值必须>=0(准确说=0,因为最后要=0),前缀和一旦小于0,那么一定是)比(多,就能对应上第二种情况了。除了这样想,也可以想题目要求两个括号相等,如果(多的话,最后的sum一定>0;如果)多的话怎么办呢?对于stack来说不能为空,对于前缀和来说就是前缀和的最小值一定不能为负。所以此题维护的是一个sum,一个lmax

法1:直观简单的记录左右需要匹配的( 和 )

#include <bits/stdc++.h>
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define dec(a,b,c) for(int a=b;a>=c;a--)
#define x first
#define y second
#define pb push_back
#define LL long long
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define INF 0x3f3f3f3f
#define PII pair<int,int>
#define pi acos(-1)
#define PIP pair<int,pair<int,int>>
using namespace std;

const int N=2e5+10;
int n,q;
char s[N];

struct Node
{
	int l,r;
	int kl=0,kr=0;
}tr[N*4];
void pushup(int u)
{
	tr[u].kl=max(0,tr[u<<1].kl-tr[u<<1|1].kr)+tr[u<<1|1].kl;
	tr[u].kr=max(0,tr[u<<1|1].kr-tr[u<<1].kl)+tr[u<<1].kr;
}

void build(int u,int l,int r)
{
	
	tr[u].l=l,tr[u].r=r;
	if(l==r){
		if(s[l]=='(')
			tr[u].kl++;
		else tr[u].kr++;
		return;
	}
	int mid=l+r>>1;
	build(u<<1,l,mid),build(u<<1|1,mid+1,r);
	pushup(u);
}

Node num(Node l,Node r){
	Node m;
	m.kl=max(0,l.kl-r.kr)+r.kl;
	m.kr=max(0,r.kr-l.kl)+l.kr;
	return m;
}

Node query(int u,int l,int r)
{
	if(tr[u].l>=l&&tr[u].r<=r) return tr[u];
	
	int mid=tr[u].l+tr[u].r>>1;
	Node k;
	if(l<=mid) k=query(u<<1,l,r);
	if(r>mid) k=num(k,query(u<<1|1,l,r));
	return k;
} 

void change(int u,int k){
	if(tr[u].l==tr[u].r){
		if(s[tr[u].l]=='(')
			tr[u].kl--,tr[u].kr++;
		else tr[u].kl++,tr[u].kr--;
		return;
	}
	int mid=tr[u].l+tr[u].r>>1;
	if(k>=tr[u].l&&k<=mid)
		change(u<<1,k);
	else
		change(u<<1|1,k);
	pushup(u);
}

int main(){
	ios;
	
	cin>>n>>q>>s+1;
	build(1,1,n);
	
	while(q--){
		int num,l,r;
		cin>>num>>l>>r;
		if(num==1){
			if(s[l]==s[r])
				continue;
			else{
				change(1,l),change(1,r);
				swap(s[l],s[r]);
			}
		}
		else{
			Node k=query(1,l,r);
			if(k.kl||k.kr)
				cout<<"No\n";
			else cout<<"Yes\n";
		}
	}
	
	return 0;
}

法2:

#include<bits/stdc++.h>
#define PII pair<int,int>
#define PLL pair<long long,long long>
#define fi first
#define se second
#define endl '\n'
#define bug printf("bug\n");
using namespace std;
const int N=2e5+10;
const int INF=0x3f3f3f3f;
const long long LNF=0x3f3f3f3f3f3f3f3f;
const long long mod=1e9+7;
struct node{
    long long l,r,sum,minn;
}tr[N<<2];
int n,m,cnt=1,last=0;
char s[N];
int st[N];
void pushup(int u){
    tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
    tr[u].minn=min(tr[u<<1].minn,tr[u<<1].sum+tr[u<<1|1].minn);
}
void bulid(int u,int l,int r){
    if(l==r){
        tr[u]={l,r,st[l],st[l]};
        printf("%d %d %d %d\n",l,r,tr[u].sum,tr[u].minn);
    }
    else{
        tr[u]={l,r,0,LNF};
        int mid=(tr[u].l+tr[u].r)>>1;
        bulid(u<<1,l,mid); bulid(u<<1|1,mid+1,r);
        pushup(u);
        //printf("%d %d %d %d\n",l,r,tr[u].sum,tr[u].minn);
    }
}
void update(int u,int pos,int val){
    if(tr[u].l==tr[u].r){
        tr[u].sum=tr[u].minn=val;
    }
    else{
        int mid=(tr[u].l+tr[u].r)>>1;
        if(pos<=mid) update(u<<1,pos,val);
        if(pos>mid) update(u<<1|1,pos,val);
        pushup(u);
    }
}
PII query(int u,int l,int r){
    if(l<=tr[u].l&&r>=tr[u].r){
        //printf("1 %d %d %d %d\n",tr[u].l,tr[u].r,tr[u].sum,tr[u].minn);
        return {tr[u].sum,tr[u].minn};
    }
    PII ls,rs;
    int mid=(tr[u].l+tr[u].r)>>1;
    if(l<=mid) ls=query(u<<1,l,r);
    if(r>mid) rs=query(u<<1|1,l,r);
    //printf("ls=%d %d")
    //printf("2 %d %d %d %d\n",tr[u].l,tr[u].r,ls.sum+rs.sum,min(ls.minn,ls.sum+rs.minn));
    return {ls.fi+rs.fi,min(ls.se,ls.fi+rs.se)};
}
int main(){
    scanf("%d %d",&n,&m);
    scanf("%s",s+1);
    for(int i=1;i<=n;i++){
        if(s[i]=='(') st[i]=1;
        else st[i]=-1;
    }
    //for(int i=1;i<=n;i++) printf("%d ",st[i]); puts("");
    bulid(1,1,n);
    while(m--){
        int a,b,c; scanf("%d %d %d",&a,&b,&c);
        if(a==1){
            swap(st[b],st[c]);
            update(1,b,st[b]); update(1,c,st[c]);
        }
        else{
            PII res=query(1,b,c);
            //printf("%d %d\n",res.sum,res.minn);
            if(res.fi==0&&res.se>=0) printf("Yes\n");
            else printf("No\n");
        }
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值