zoukankan      html  css  js  c++  java
  • 洛谷 P3797 妖梦斩木棒 解题报告

    P3797 妖梦斩木棒

    妖梦是住在白玉楼的半人半灵,拥有使用剑术程度的能力。

    题目描述

    有一天,妖梦正在练习剑术。地面上摆放了一支非常长的木棒,妖梦把它们切成了等长的(n)段。现在这个木棒可以看做由三种小段构成,中间的n-2段都是左右都被切断的断头,我们记做’(X)’,最左边的一段和最右边的一段各有一个圆头,记做’(‘和’)’。幽幽子吃饱后闲来无事,决定戏弄一下妖梦。她拿来了许多这样的三种小段木棒,来替换掉妖梦切下来的n段中的一部分,然后问妖梦一些问题。这些操作可以这样描述:

    (1) (x) (C) 将第(x)个小段的木棒替换成(C)型,C只会是’(X)’,’(()‘,’())’中的一种

    (2) (l) (r) 询问妖梦从第(l)段到第(r)段之间(含(l),(r)),有多少个完整的木棒

    完整的木棒左右两端必须分别为’(‘和’)’,并且中间要么什么都没有,要么只能有’(X)’。

    虽然妖梦能够数清楚这些问题,但幽幽子觉得她回答得太慢了,你能教给妖梦一个更快的办法吗?

    输入输出格式

    输入格式:

    第一行两个整数(n)(m)(n)表示共有(n)段木棒,(m)表示有(m)次操作。

    木棒的初始形状为(XXXXXX......XXXXXX)。

    接下来(m)行,每行三个整数/字符,用空格隔开。第一个整数为1或2,表示操作的类型,若类型为1,则接下来一个整数x,一个字符C。若类型为2,接下来两个整数(l),(r)。含义见题目描述。

    输出格式:

    对于每一个操作2,输出一行一个整数,表示对应询问的答案。

    说明

    对于30%的数据,1<=n,m<=1000

    对于100%的数据,1<=n,m<=200000

    by-orangebird


    拍了一下午总算搞出来了,太毒了。

    首先说一下思想,其实大概一看大家都会想到拿线段树维护,但实际上没见过类似的模型是比较难想到正解的。

    我个人认为,这个维护是有一点分治的思想的(不过线段树其实也是分治的思想了emmm)

    在线段树的区间里维护3个值(dat,L,R),分别代表区间木棍数量,最左边的右括号的坐标(没有为即为0),最右边的左括号的坐标(没有即为n+1)

    对询问
    左儿子的+右儿子的+合并时可能产生的一个

    对修改
    (dat)域和询问一样
    (L)(R)一样,说一个吧

    比如(L),先拿左儿子更新,如果没更新成功(指没有左儿子括号)再尝试右儿子更新

    卡点就在什么时候更新上。当然可以维护一个额外的域去判断,但不加一个额外的域时还是很坑的。。。

    不直接说更新方法了,想不出来可以直接看看代码


    Code:

    #include <cstdio>
    #define ls id<<1
    #define rs id<<1|1
    const int N=200010;
    int n,m;
    int dat[N<<2],L[N<<2],R[N<<2];
    void change(int id,int l,int r,int x,int delta)
    {
        if(l==r)
        {
            L[id]=0;R[id]=n+1;
            if(delta==1)//'('
                R[id]=l;//最右边的左括号
            else if(delta==2)//')'
                L[id]=r;//最左边的右括号
            return;
        }
        int mid=l+r>>1;
        if(x<=mid)
            change(ls,l,mid,x,delta);
        else
            change(rs,mid+1,r,x,delta);
        dat[id]=dat[ls]+dat[rs]+(R[ls]<=n&&L[rs]);
        L[id]=L[ls];
        //如果左儿子没有左括号和右括号就可以考虑用右儿子的右括号了
        if(dat[ls]==0&&R[ls]==n+1&&L[ls]==0)
            L[id]=L[rs];
        R[id]=R[rs];
        if(dat[rs]==0&&L[rs]==0&&R[rs]==n+1)
            R[id]=R[ls];
    }
    int query(int id,int l,int r,int ll,int rr)
    {
        if(l==ll&&r==rr)
            return dat[id];
        int mid=ll+rr>>1;
        if(r<=mid)
            return query(ls,l,r,ll,mid);
        else if(l>mid)
            return query(rs,l,r,mid+1,rr);
        else
            return query(ls,l,mid,ll,mid)+query(rs,mid+1,r,mid+1,rr)+(R[ls]<=n&&L[rs]&&R[ls]>=l&&L[rs]<=r);
    }
    void build(int id,int l,int r)
    {
        L[id]=0,R[id]=n+1;
        if(l==1) R[id]=1;//最右边的左括号位置为1
        if(r==n) L[id]=n;//最左边的右括号位置为n
        if(l==r) return;
        int mid=l+r>>1;
        build(ls,l,mid);
        build(rs,mid+1,r);
    }
    int main()
    {
        //freopen("data.in","r",stdin);
        //freopen("wr.out","w",stdout);
        scanf("%d%d",&n,&m);
        int opt,l,r;char c;
        dat[1]=1;
        build(1,1,n);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&opt,&l);
            if(opt==1)
            {
                scanf(" ");
                scanf("%c",&c);
                if(c=='X') change(1,1,n,l,0);
                else if(c=='(') change(1,1,n,l,1);
                else change(1,1,n,l,2);
            }
            else
            {
                scanf("%d",&r);
                printf("%d
    ",query(1,l,r,1,n));
            }
        }
        return 0;
    }
    

    2018.7.9

  • 相关阅读:
    网站结构之扁平结构与树形结构的区分
    如何提高网站的访问速度
    CSS透明度大汇总
    Microsoft.AlphaImageLoader滤镜讲解
    浏览器的渲染原理简介
    ACM思维题训练 Section A
    CF--思维练习--CodeForces
    CF--思维练习--CodeForces
    CF--思维练习--CodeForces
    CF思维联系--CodeForces
  • 原文地址:https://www.cnblogs.com/butterflydew/p/9285079.html
Copyright © 2011-2022 走看看