线段树 P2253 好一个一中腰鼓!

传送门

不得不说 这真是一道不错的线段树的题目

这一道题的大意就是说

一开始所有的状态均为0

会有m次指令

每一次可以把一个点的状态进行更改

原来是0就变成1

原来是1就变成0

为了锻炼代码能力 我决定还是中规中矩地写线段树

这一道题还规定了一种串  就是0和1间隔交替 (比如010101  101 01010)

你每一次更改之后 要求出所有的点组成的序列中最长的连续“01”串

那么这一道题如果用线段树来做 转移会稍微复杂一点

我们定义sum为当前区间最长的01串

lsum为当前区间最长的前缀01串

rsum为当前区间最长的后缀01串

那我们怎么进行转移呢

先说sum

sum可以是左区间的sum、右区间sum的最大值

也可以是左区间的右半部分(后缀)和右区间的左半部分(前缀)  这两个01串拼起来

当然 这也有一定的条件 必须左半部分的最后一个字符与右半区间的第一个字符是不相同的 拼接起来之后才合法

然后lsum怎么转移?

首先肯定可以是左半区间的lsum转移过来

但是还有一种情况

就是如果左半区间整个都是合法的01串(也就是左半区间的前缀长度 就等于 整个左半区间的长度)

那么和起来之后最长的前缀01串有可能是整个左半区间+右半区间的前缀串

同样 这也有一定的条件 就是必须左半区间的最后一个字符等于右半区间的第一个字符

rsum则与lsum同理

有可能是右半区间的rsum转移过来

也有可能是整个右半区间+左半区间的后缀

条件与lsum的类似

这样子我们就非常完美地建出了一棵线段树

这样子我们的解法其实更加高级

题目可以随便询问任何一个区间 我都可以非常自信地回答出该区间内合法的01串的长度

接下来就上我的宏伟代码啦

(其实我也非常惊讶为什么我10分钟就做出来了。。一遍AC。。)

//P2253 好一个一中腰鼓!
#include<bits/stdc++.h>
#define maxn 20005
using namespace std;
int Color[maxn];
struct node{
    int l,r,sum,lsum,rsum;
}t[maxn<<2];
void build(int p,int l,int r)
{
    t[p].l=l;t[p].r=r;
    if(l==r)
    {
        t[p].sum=t[p].lsum=t[p].rsum=1;
        return;
    }
    int mid=l+r>>1;
    build(p*2,l,mid);
    build(p*2+1,mid+1,r);
    t[p].sum=max(t[p*2].sum,t[p*2+1].sum);
    if(Color[t[p*2].r]!=Color[t[p*2+1].l]) t[p].sum=max(t[p].sum,t[p*2].rsum+t[p*2+1].lsum);
    t[p].lsum=t[p*2].lsum;
    if(t[p*2].lsum==(t[p*2].r-t[p*2].l+1)&&Color[t[p*2].r]!=Color[t[p*2+1].l])
        t[p].lsum=max(t[p].lsum,t[p*2].r-t[p*2].l+1+t[p*2+1].lsum);
    t[p].rsum=t[p*2+1].rsum;
    if(t[p*2+1].rsum==(t[p*2+1].r-t[p*2+1].l+1)&&Color[t[p*2].r]!=Color[t[p*2+1].l])
        t[p].rsum=max(t[p].rsum,t[p*2+1].r-t[p*2+1].l+1+t[p*2].rsum);
    return;
}
void Change(int p,int l,int r,int Ind)
{
    if(l==Ind&&r==Ind)
    {
        Color[Ind]=1-Color[Ind];
        return;
    }
    int mid=l+r>>1;
    if(Ind>mid)
        Change(p*2+1,mid+1,r,Ind);
    else Change(p*2,l,mid,Ind);
    t[p].sum=max(t[p*2].sum,t[p*2+1].sum);
    if(Color[t[p*2].r]!=Color[t[p*2+1].l]) t[p].sum=max(t[p].sum,t[p*2].rsum+t[p*2+1].lsum);
    t[p].lsum=t[p*2].lsum;
    if(t[p*2].lsum==(t[p*2].r-t[p*2].l+1)&&Color[t[p*2].r]!=Color[t[p*2+1].l])
        t[p].lsum=max(t[p].lsum,t[p*2].r-t[p*2].l+1+t[p*2+1].lsum);
    t[p].rsum=t[p*2+1].rsum;
    if(t[p*2+1].rsum==(t[p*2+1].r-t[p*2+1].l+1)&&Color[t[p*2].r]!=Color[t[p*2+1].l])
        t[p].rsum=max(t[p].rsum,t[p*2+1].r-t[p*2+1].l+1+t[p*2].rsum);

}
void Ask(){
    printf("%d\n",t[1].sum);
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    build(1,1,n);
    while(m--)
    {
        int Index;
        scanf("%d",&Index);
        Change(1,1,n,Index);
        Ask();
    }
    return 0;
}

敲之甚急 代码凌乱 请见谅

原文地址:https://www.cnblogs.com/akioi/p/12215200.html

时间: 2024-10-19 04:20:23

线段树 P2253 好一个一中腰鼓!的相关文章

洛谷 P2253 好一个一中腰鼓 --线段树

P2253 好一个一中腰鼓 --线段树 题目背景 话说我大一中的运动会就要来了,据本班同学剧透(其实早就知道了),我萌萌的初二年将要表演腰鼓[喷],这个无厘头的题目便由此而来. Ivan乱入:“忽一人大呼:‘好一个安塞腰鼓!’满座寂然,无敢哗者,遂与外人间隔.” 题目描述 设想一下,腰鼓有两面,一面是红色的,一面是白色的.初二的苏大学神想给你这个oier出一道题.假设一共有N(1<=N<=20,000)个同学表演,表演刚开始每一个鼓都是红色面朝向观众,舞蹈老师会发出M(1<=M<=

P2253 好一个一中腰鼓!

P2253 好一个一中腰鼓! 本蒟蒻第一次用线段树做连续最长子段 线段树是将一个大区间二分成两个小区间,通过递归解决两个小区间的问题,然后合并.得到大区间的解. 类比一下分治法求单个最长连续子段. 每次也都是将一个大区间分成两个小区间.分别解决完小区间后.将小区间合并,从分界点左右遍历.暴力的求连续子序列的长度. 然后将两个小区间分别作为一个区间求解的最优值和从分界点的连续子段长度比较,然后就得到了这个大区间的最长连续子段的长度. 可是,如果在线段树中呢?每次从分界点暴力的求时间复杂度太高. 那

洛谷 P2253 好一个一中腰鼓!

题目背景 话说我大一中的运动会就要来了,据本班同学剧透(其实早就知道了),我萌萌的初二年将要表演腰鼓[喷],这个无厘头的题目便由此而来. Ivan乱入:“忽一人大呼:‘好一个安塞腰鼓!’满座寂然,无敢哗者,遂与外人间隔.” 题目描述 设想一下,腰鼓有两面,一面是红色的,一面是白色的.初二的苏大学神想给你这个oier出一道题.假设一共有N(1<=N<=20,000)个同学表演,表演刚开始每一个鼓都是红色面朝向观众,舞蹈老师会发出M(1<=M<=20,000)个指令,如果指令发给第i个

洛谷P2253 好一个一中腰鼓!

题目背景 话说我大一中的运动会就要来了,据本班同学剧透(其实早就知道了),我萌萌的初二年将要表演腰鼓[喷],这个无厘头的题目便由此而来. Ivan乱入:"忽一人大呼:'好一个安塞腰鼓!'满座寂然,无敢哗者,遂与外人间隔." 题目描述 设想一下,腰鼓有两面,一面是红色的,一面是白色的.初二的苏大学神想给你这个oier出一道题.假设一共有N(1<=N<=20,000)个同学表演,表演刚开始每一个鼓都是红色面朝向观众,舞蹈老师会发出M(1<=M<=20,000)个指令

【P2253】 好一个一中腰鼓!

Description 给定一个01子串和操作数,每次操作可以使一个元素0变为1, 1变为0,求区间最长连续相邻不相等的长度 Solution 对于最长连续不相等,我们可以用几个量来维护,为: 1  lf 从左边开始的最长连续相邻不相等最大长度 2 rf 从右边开始的最长连续相邻不相等最大长度 3 mf 内部的最长连续相邻不相等最大长度 所以,最长的长度为max(tr[1].lf, tr[1].rf, tr[1].mf),对于每一个变量我们可以这样维护 tr[x].lf = max(tr[x <

luogu 1712 区间(线段树+尺取法)

题意:给出n个区间,求选择一些区间,使得一个点被覆盖的次数超过m次,最小的花费.花费指的是选择的区间中最大长度减去最小长度. 坐标值这么大,n比较小,显然需要离散化,需要一个技巧,把区间转化为半开半闭区间,然后线段树的每一个节点表示一个半开半闭区间. 接着我们注意到需要求最小的花费,且这个花费只与选择的区间集合中的最大长度和最小长度有关. 这意味着如果最大长度和最小长度一定,我们显然是需要把中间长度的区间尽量的选择进去使答案不会变的更劣. 不妨把区间按长度排序,枚举每个最小长度区间,然后最大区间

数据结构(括号序列,线段树):ZJOI 2007 捉迷藏

[题目描述] Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条双向走廊组成,这N-1条走廊的分布使得任意两个屋子都互相可达. 游戏是这样进行的,孩子们负责躲藏,Jiajia负责找,而Wind负责操纵这N个屋子的灯.在起初的时候,所有的灯都没有被打开.每一次,孩子们只会躲 藏在没有开灯的房间中,但是为了增加刺激性,孩子们会要求打开某个房间的电灯或者关闭某个房间的电灯.为了评估某一次

[POJ2155] Matrix(二维线段树,树套树)

题目链接:http://poj.org/problem?id=2155 题意:给一个01矩阵,两个操作,翻转:子矩阵里每一个数都由0变1,1变0. 查询:查询某一点是0还是1. 一直以为二维线段树就是开一个线段树数组的我- 这题暴力更新每一个小矩形,翻转就+1,最后看看某点的奇偶. 写屎了,特别注意的是在外层查询的时候要先把当前层的内层query掉,接着再向下扩展.这样外层就不用lazy啦 1 #include <algorithm> 2 #include <iostream> 3

HDU 2795 线段树单点更新

Billboard Time Limit: 20000/8000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 23498    Accepted Submission(s): 9687 Problem Description At the entrance to the university, there is a huge rectangular billboard of s