• 【题解报告】P3797 妖梦斩木棒


    前置芝士:线段树 (应该会的吧)

    本题可以算线段树的基本操作了,进入正题吧:

    我们线段树中存储三个变量:

    • (sum[x]) :完整的木棒的数量

    • (ls[x]) :最右边的括号是否是 (

    • (rs[x]) :最左边的括号是否是 )

    做题过程分为建树,单点修改和查询 (很经典了吧)

    本题在建树和单点修改时只有一点与其他节点不同,就是合并节点信息,在这里略作解释:

    首先是最简单直接赋值:

    (rs_x=rs_{sonL},ls_x=ls_{sonR})

    (sum_x=sum_{sonL}+sum_{sonR}+[ls_{sonL}==1&&rs_{sonR}==1])

    另外注意一点,当(sonL)的区集中全是(X)时,(rs_x)是要等于(rs_{sonR})的,而且易证得,如果(sum_{sonL}==0&&rs_{sonL}==0&&ls_{sonL}==0),则可以说明左儿子全是(X),因此合并部分代码如下:

    inline void update(int x)
    {
    	rs[x]=rs[x<<1],ls[x]=ls[x<<1|1];
    	if(sum[x<<1]==0&&ls[x<<1]==0&&rs[x<<1]==0) rs[x]=rs[x<<1|1];
    	if(sum[x<<1|1]==0&&ls[x<<1|1]==0&&rs[x<<1|1]==0) ls[x]=ls[x<<1];
    	sum[x]=sum[x<<1]+sum[x<<1|1]+(ls[x<<1]+rs[x<<1|1]>>1);
    }
    

    再就是查询了,这里需要用到一个小技巧,线段树的遍历顺序是从左往右的,所以我们每遇到一个合法区间,和前一个遇到的合法区间合并计算答案就好了,同样需要注意全为(X)的情况。

    (OVER)~~

    完整代码如下:

    #include<bits/stdc++.h>
    #define re register
    using namespace std;
    inline int read()
    {
    	re int x=0,f=1;
    	re char ch=getchar();
    	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') f*=-1;
    	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
    	return x*f;
    }
    const int N=200005;
    int n,m,sum[N<<2],ans;
    bool ls[N<<2],rs[N<<2],last;
    char ch[N],s[1];
    inline void update(int x)
    {
    	rs[x]=rs[x<<1],ls[x]=ls[x<<1|1];
    	if(sum[x<<1]==0&&ls[x<<1]==0&&rs[x<<1]==0) rs[x]=rs[x<<1|1];
    	if(sum[x<<1|1]==0&&ls[x<<1|1]==0&&rs[x<<1|1]==0) ls[x]=ls[x<<1];
    	sum[x]=sum[x<<1]+sum[x<<1|1]+(ls[x<<1]+rs[x<<1|1]>>1);
    }
    void build(int l,int r,int x)
    {
    	if(l==r) {ls[x]=(ch[l]=='('),rs[x]=(ch[l]==')');return ;}
    	re int mid=l+r>>1;
    	build(l,mid,x<<1),build(mid+1,r,x<<1|1);
    	update(x);
    }
    void change(int l,int r,int x,int p,char a)
    {
    	if(l==r) {ls[x]=(a=='('),rs[x]=(a==')');return ;}
    	re int mid=l+r>>1;
    	if(p<=mid) change(l,mid,x<<1,p,a);
    	else change(mid+1,r,x<<1|1,p,a);
    	update(x);
    }
    void ask(int l,int r,int x,int L,int R)
    {
    	if(L<=l&&r<=R)
    	{
    		if(sum[x]==0&&ls[x]==0&&rs[x]==0) return ;
    		ans+=sum[x]+(last+rs[x]>>1);
    		last=ls[x];//last为上一个合法区间最右边一个括号是否为 (
    		return ;
    	}
    	re int mid=l+r>>1;
    	if(L<=mid) ask(l,mid,x<<1,L,R);
    	if(R>mid) ask(mid+1,r,x<<1|1,L,R);
    }
    inline void Fre()
    {
    	freopen("P3797.in","r",stdin);
    	freopen("P3797.out","w",stdout);
    }
    int main()
    {
    	Fre();
    	n=read(),m=read();
    	for(re int i=2;i<n;i++) ch[i]='X';ch[1]='(',ch[n]=')';
    	build(1,n,1);
    	for(re int i=1,type,x,y;i<=m;i++)
    	{
    		type=read(),x=read();
    		if(type==1)
    		{
    			scanf("%s",s);
    			change(1,n,1,x,s[0]);
    		}
    		else
    		{
    			y=read(),ask(1,n,1,x,y);
    			printf("%d
    ",ans);
    			ans=last=0;
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    asp.net 中@Html.Partial,@Html.Action,@Html.RenderPartial,@Html.RenderAction
    mvc EF 执行SQL语句
    MVC 自定义AuthorizeAttribute实现权限管理
    EF
    sql 、linq、lambda 查询语句的区别
    说说C#中的enum吧
    C# A窗口内容显示在B窗口中的方法
    C# 程序开始主要是写类和方法 的基本步骤和调用方法
    C# 文件流基本操作步骤
    Ghost命令使用方法
  • 原文地址:https://www.cnblogs.com/jkzcr01-QAQ/p/13847508.html
Copyright © 2020-2023  润新知