首页 > 其他分享 >洛谷题单指南-线段树-P5522 [yLOI2019] 棠梨煎雪

洛谷题单指南-线段树-P5522 [yLOI2019] 棠梨煎雪

时间:2024-12-11 09:12:22浏览次数:7  
标签:Node P5522 int 洛谷题 棠梨 位置 v1 v2 字符串

原题链接:https://www.luogu.com.cn/problem/P5522

题意解读:有若干0/1/?组成的字符串,支持两种操作:1.将制定位置字符串修改成新字符串;2.查询区间内字符串能否统一成一个字符串,求有多少种可能;将2的所有结果异或起来,再和0异或,输出最终答案。注意:?表示可以用0或1取代。

解题思路:单点修改,区间查询,可以用线段树解决。

关键看线段树节点维护什么信息?

字符串要能统一成一个,对应位置字符必须符合:1.都是'1' 2.都是'0' 3.有一个是'?'

对于1/2是固定的,对于3,如果一个是'?'一个是非'?'那么情况也是唯一的,如果两个都是'?'那么既可以都是'1'又可以都是'0',这样就会有2中可能

因此对于两个字符串能统一成多少种相同的串,就是要找到有多少个位置都是'?',设有x个,则一共有2^x中可能。

如果直接维护字符串,在进行pushup等操作的时候每次常数都比较高,需要一种更优化的方式,如果将对应位置的值映射到二进制就好处理了。

方式如下:

设v1是一个整数,其二进制表示一个01?字符串中确定是'1'的位置为1,如0?1,第2个位置是'1',那么v1二进制为100,v1=4

设v2是一个整数,其二进制表示一个01?字符串中确定是'0'的位置为1,如0?1,第0个位置是'0',那么v2二进制为001,v2=1

有了以上定义,

1、在进行节点合并时,root.v1 = left.v1 | right.v1,root.v2 = left.v2 | right.v2

2、在进行区间查询时,直接返回合并后的结果即可,再进一步进行判断和统计?的个数,

- 判断逻辑:如果v1 & v2不为0,说明多个字符串相同位置既有'1'又有'0',不可能统一成一种,结果是0

- 统计方式:如果v1 & v2为0,将v1 | v2,统计其中字符串长度范围内0的个数x,即表示'?'的个数,再取2^x,即表示可以统一成多少种字符串

3、单点修改操作就是常规的修改,注意将字符串转化成v1、v2

具体细节参考代码。

100分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 100010;

struct Node
{
    int l, r;
    int v1; //v1的二进制表示中,[l,r]区间内所有字符串位置i为'1'才是1
    int v2; //v1的二进制表示中,[l,r]区间内所有字符串位置i为'0'才是1
} tr[N * 4];
string a[N];
int n, m, q, ans;

void pushup(Node &root, Node &left, Node &right)
{
    root.v1 = left.v1 | right.v1;
    root.v2 = left.v2 | right.v2;
}

void pushup(int u)
{
    pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
}

void build(int u, int l, int r)
{
    tr[u] = {l, r};
    if(l == r)
    {
        for(int i = 0; i < a[l].length(); i++)
        {
            if(a[l][i] == '1') tr[u].v1 |= (1 << i);
            else if(a[l][i] == '0') tr[u].v2 |= (1 << i);
        }
    }
    else
    {
        int mid = l + r >> 1;
        build(u << 1, l, mid);
        build(u << 1 | 1, mid + 1, r);
        pushup(u);
    }
}

Node query(int u, int l, int r)
{
    if(tr[u].l >= l && tr[u].r <= r) return tr[u];
    else if(tr[u].l > r || tr[u].r < l) return Node{};
    else
    {
        Node res = {};
        Node left = query(u << 1, l, r);
        Node right = query(u << 1 | 1, l, r);
        pushup(res, left, right);
        return res;
    }
}

void update(int u, int pos, string t)
{
    if(tr[u].l == tr[u].r) 
    {
        tr[u].v1 = tr[u].v2 = 0; //先清空
        for(int i = 0; i < t.length(); i++)
        {
            if(t[i] == '1') tr[u].v1 |= (1 << i);
            else if(t[i] == '0') tr[u].v2 |= (1 << i);
        }
    }
    else
    {
        int mid = tr[u].l + tr[u].r >> 1;
        if(pos <= mid) update(u << 1, pos, t);
        else update(u << 1 | 1, pos, t);
        pushup(u);
    }
}

int main()
{
    cin >> n >> m >> q;
    for(int i = 1; i <= m; i++) cin >> a[i];
    build(1, 1, m);
    int op, l, r, pos;
    string t;
    while(q--)
    {
        cin >> op;
        if(op == 0)
        {
            cin >> l >> r;
            Node res = query(1, l, r);
            if(res.v1 & res.v2) //说明有'1'的位置也有'0',不能统一成一个字符串
            {
                ans ^= 0; //结果是0
            }
            else
            {
                int x = res.v1 | res.v2;
                int cnt = 0;
                for(int i = 0; i < n; i++)
                {
                    if(x >> i & 1) cnt++; //统计确定是'1'或'0'的位置数量
                }
                int ques = n - cnt; //'?'的个数
                ans ^= (1 << ques); // 2^ques即有多少种字符串组合
            }
        }
        else
        {
            cin >> pos >> t;
            update(1, pos, t);
        }
    }
    cout << ans;
    return 0;
}

 

标签:Node,P5522,int,洛谷题,棠梨,位置,v1,v2,字符串
From: https://www.cnblogs.com/jcwy/p/18598536

相关文章

  • 洛谷题单python解【入门2】分支结构
    P2433【深基1-2】小学数学N合一importmathn=int(input())ifn==1:print('IloveLuogu!')elifn==2:print(2+4,10-2-4)elifn==3:print(14//4)print(14-14%4)print(14%4)elifn==4:print("%.3f"%(500/3))elifn==5:......
  • 洛谷题单python解【入门1】顺序结构
    B2002Hello,World print("Hello,World!")B2025输出字符菱形print("*")print("***")print("*****")print("***")print("*")P1000超级玛丽游戏print("""********......
  • 洛谷题单指南-线段树-P1558 色板游戏
    原题链接:https://www.luogu.com.cn/problem/P1558题意解读:给定序列a[n],初始都为1,支持两种操作:1.将区间[a,b]所有值都改为c;2.查询区间[a,b]范围有多少个不同的数;输出所有操作2的结果。解题思路:又是线段树的典型应用,要支持区间修改,需要用到懒标记。首先,本题中,颜色一共有1~30种,要......
  • 洛谷题单指南-线段树-P1637 三元上升子序列
    原题链接:https://www.luogu.com.cn/problem/P1637题意解读:统计序列a[1]~a[n]中三元上升子序列的个数,三元上升子序列是指对于1<=i<j<k<=n有a[i]<a[j]<a[k],(a[i],a[j],a[k])成为一组上升子序列。解题思路:1、先思考一下暴力,通过三重循环枚举i,j,k找到所有i<j<k时符合a[i]<a[j]<a[k]......
  • 洛谷题单指南-线段树-P6492 [COCI2010-2011#6] STEP
    原题链接:https://www.luogu.com.cn/problem/P6492题意解读:一个序列,初始L,可以指定一个位置修改,L修改成R,R修改成L,可以令L=0,R=1,然后每次修改后输出序列最长不连续0、1(0/1交替出现)的长度。解题思路:序列支持单点修改(0->1,1->0),区间查询(最长不连续0、1长度),因此可以采用线段树,不需要懒标......
  • 洛谷题单指南-线段树-P1471 方差
    原题链接:https://www.luogu.com.cn/problem/P1471题意解读:给定序列a[n],支持三种操作:1.将区间每个数加上一个数2.查询区间的平均数3、查询区间的方差解题思路:要支持区间修改和查询,首选线段树,下面看线段树节点需要维护的信息平均数=区间和/n,所以第一个要维护的信息是区间和......
  • 洛谷题单指南-线段树-P4513 小白逛公园
    原题链接:https://www.luogu.com.cn/problem/P4513题意解读:给定序列a[n],支持两种操作:1.查询区间[l,r]内的最大子段和2.将a[x]修改成s,输出其中每一个查询操作的结果。解题思路:区间问题依然想到线段树,问题主要在于线段树的节点要维护哪些信息:最直接的,肯定要维护节点所表示区间的......
  • 洛谷题单指南-线段树-P3373 【模板】线段树 2
    原题链接:https://www.luogu.com.cn/problem/P3373题意解读:对于序列a[n],支持三种操作:1.对区间每个数乘上一个数2.对区间每个数加上一个数3.求区间和解题思路:由于支持乘、加两种区间修改操作,是线段树的另一种典型应用:多个懒标记显然,这里需要两个懒标记,mul表示对子节点区间每个......
  • 洛谷题单指南-线段树-P1253 扶苏的问题
    原题链接:https://www.luogu.com.cn/problem/P1253题意解读:对于一个序列a[n],支持三种操作:1.将区间[l,r]所有数设置为x;2.将区间[l,r]所有数加上x;3.查询区间[l,r]的最大值解题思路:典型的线段树求解区间问题。线段树节点需要维护如下关键信息:1、区间l,r2、区间最大值v3、懒标记se......
  • 洛谷题单指南-线段树-P1438 无聊的数列
    原题链接:https://www.luogu.com.cn/problem/P1438题意解读:给定序列a[n],支持两种操作:1.给区间[l,r]每个数增加一个对应位置等差数列的元素,首项k,公差d;2.查询第x个元素值解题思路:直接用线段树求解。要实现区间修改,需要引入懒标记,而这里修改的值是要增加一个等差数列的某一项,需要保......