zoukankan      html  css  js  c++  java
  • Luogu P2572 序列操作

    (是道线段树好题√)

    题目链接

    题外话:这道题我也不知道卡了自己多少天,从初赛之前就开始做,一直到现在才a掉(时间跨度得有将近十天了吧?)

    线段树,嗯,好像很简单的样子。

    但事实上因为自己太菜了,卡了好久;

    第一遍的思路简单的很,因为完全没有考虑标记下传的顺序问题,qf(取反)标记和chg(修改)标记各自下传各自的,于是乎就一直10分10分(没有好好写线段树Ⅱ的锅),咋改都不对,也没看出自己错在哪了;

    然后被建议重构代码,于是尝试暴力出奇迹,(O(nm)的暴力+O_2)结果拿到了90pts??(jyy问号),特别迷惑;

    Solution:

    共有5个操作:(分别为

    0 a b 把[a, b]区间内的所有数全变成0

    1 a b 把[a, b]区间内的所有数全变成1

    2 a b 把[a,b]区间内的所有数全部取反,也就是说把所有的0变成1,把所有的1变成0

    3 a b 询问[a, b]区间内总共有多少个1

    4 a b 询问[a, b]区间内最多有多少个连续的1

    要想完成这以上五个操作,在线段树上需要维护以下信息:

    (t[k].sum) 记录k节点对应区间共有多少个1

    (t[k].L[0/1])分别记录k节点对应区间的左边有几个连续的0/1

    (t[k].R[0/1])分别记录k节点对应区间的右边有几个连续的0/1

    ↑维护左右连续0/1的个数,是因为在合并两个区间时,最长的连续的个数有可能来自左区间右侧和右区间左侧的合并

    (t[k].mx[0/1])记录k节点对应区间最长连续0/1的个数

    两个标记:

    (t[k].qf) 取反标记 0>不取反 1>取反

    $t[k].chg $ 修改标记 -1>不修改 0> 修改为0 1==>修改为1

    对于(t[k].L[0/1],t[k].R[0/1],t[k].mx[0/1])的维护需要注意区间合并时可能会产生更长的连续段,要考虑并且维护,建树、修改、查询以及对应的重新维护区间的update此处省略…字,下面重点讲pushdown:

    因为有两个不同的标记,在下传标记时要考虑先下传哪一个:

    首先要明确是区间对于这棵线段树来说,取反标记和修改标记是无法通过modify函数的修改同时存在的(都pushdown掉了所以不会同时存在w)

    啊,是凌乱的lz,我该怎么讲明白这个标记下传的问题

    先讲下传规则再来感性李姐叭:

    1.对于一个既有修改标记又有取反标记的节点,我们忽略取反标记,只下传修改标记

    2.如果只有取反标记,又要分为两种情况:

    1. 要下传的子节点有修改标记,那么直接将修改标记取反,不下传取反标记;
    2. 要下传的子节点没有修改标记,将子节点的取反标记取反;

    然后交换0/1的信息(swap大法好√)

    最后不要忘记将当前节点的标记清空。

    所以如果既有修改标记又有取反标记,那么取反标记一定是通过它的祖先节点下传的,而在下传时,修改标记的值就已经相应的被取反了,所以不需要再下传取反标记了w;

    最后,是老Re lz的代码(是码风清奇的奇女子将就着看叭):

    Code:

    #include<bits/stdc++.h>
    
    using namespace std;
    
    inline int read() {
    	int ans=0;
    	char last=' ',ch=getchar();
    	while(ch>'9'||ch<'0') last=ch,ch=getchar();
    	while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
    	if(last=='-') ans=-ans;
    	return ans;
    }
    const int mxn=200010;
    
    int m,n,maxn;
    int x[mxn];
    struct node {
    	int sum;
    	int L[2],R[2];
    	int qf,chg;
    	int mx[2];
    } t[mxn<<2];
    
    void update(int k,int l,int r) {
    	t[k].sum=t[k<<1].sum+t[k<<1|1].sum;
    	int mid=(l+r)>>1;
    	for(int i=0; i<=1; i++) {
    		t[k].L[i]=t[k<<1].L[i];
    		if(t[k<<1].L[i]==mid-l+1)
    			t[k].L[i]+=t[k<<1|1].L[i];
    
    		t[k].R[i]=t[k<<1|1].R[i];
    		if(t[k<<1|1].R[i]==r-mid)
    			t[k].R[i]+=t[k<<1].R[i];
    
    		t[k].mx[i]=max(t[k<<1].mx[i],max(t[k<<1|1].mx[i],t[k<<1].R[i]+t[k<<1|1].L[i]));
    	}
    }
    
    void build(int k,int l,int r) {
    	t[k].chg=-1;
    	t[k].qf=0;
    	if(l==r) {	
    		t[k].L[0]=t[k].R[0]=t[k].mx[0]=x[l]==0;
    		t[k].L[1]=t[k].R[1]=t[k].mx[1]=x[l]==1;
    		if(x[l]) t[k].sum=1;
    		return;
    	}
    	
    	int mid=(l+r)>>1;
    	build(k<<1,l,mid);
    	build(k<<1|1,mid+1,r);
    	update(k,l,r);
    }
    
    void pushdown(int k,int l,int r) {
    	int mid=(l+r)>>1;
    	if(t[k].chg!=-1) {
    		int p=t[k].chg; 
    		t[k].qf=0;
    		t[k<<1].chg=t[k<<1|1].chg=p;
    		t[k<<1].qf=t[k<<1|1].qf=0;
    		t[k<<1].sum=(mid-l+1)*p;
    		t[k<<1|1].sum=(r-mid)*p;
    		t[k<<1].L[p]=t[k<<1].mx[p]=t[k<<1].R[p]=mid-l+1;
    		t[k<<1|1].L[p]=t[k<<1|1].mx[p]=t[k<<1|1].R[p]=r-mid;
    		t[k<<1].L[p^1]=t[k<<1].mx[p^1]=t[k<<1].R[p^1]=0;
    		t[k<<1|1].L[p^1]=t[k<<1|1].mx[p^1]=t[k<<1|1].R[p^1]=0;
    		t[k].chg=-1;
    	}
    	if(t[k].qf) {
    		t[k<<1].sum=mid-l+1-t[k<<1].sum;
    		t[k<<1|1].sum=r-mid-t[k<<1|1].sum;
    		if(t[k<<1].chg!=-1) 
    			t[k<<1].chg^=1;
    		else 
    			t[k<<1].qf^=1;
    			
    		if(t[k<<1|1].chg!=-1) 
    			t[k<<1|1].chg^=1;
    		else
    			t[k<<1|1].qf^=1;
    		swap(t[k<<1].L[0],t[k<<1].L[1]); 
    		swap(t[k<<1].R[0],t[k<<1].R[1]);
    		swap(t[k<<1].mx[0],t[k<<1].mx[1]);
    		
    		swap(t[k<<1|1].L[0],t[k<<1|1].L[1]);
    		swap(t[k<<1|1].R[0],t[k<<1|1].R[1]);
    		swap(t[k<<1|1].mx[0],t[k<<1|1].mx[1]);
    		
    		t[k].qf=0;
    	}
    }
    
    void modify(int k,int l,int r,int x,int y,int q) {
    	pushdown(k,l,r);
    	if(x<=l&&r<=y) {
    		if(q==1||q==0) {
    			t[k].sum=(r-l+1)*q;
    			t[k].L[q]=t[k].R[q]=t[k].mx[q]=r-l+1;
    			t[k].L[q^1]=t[k].R[q^1]=t[k].mx[q^1]=0;
    			t[k].chg=q;
    		} else {
    			t[k].sum=(r-l+1)-t[k].sum;
    			t[k].qf^=1;
    			swap(t[k].L[0],t[k].L[1]);
    			swap(t[k].R[0],t[k].R[1]);
    			swap(t[k].mx[0],t[k].mx[1]);
    		}
    		return;
    	}
    	
    	int mid=(l+r)>>1;
    	if(x<=mid) modify(k<<1,l,mid,x,y,q);
    	if(y>mid) modify(k<<1|1,mid+1,r,x,y,q);
    	update(k,l,r);
    }
    
    int query(int k,int l,int r,int x,int y) {
    	pushdown(k,l,r);
    	if(x<=l&&r<=y)
    		return t[k].sum;
    		
    	int mid=(l+r)>>1;
    	
    	int rtn=0;
    	if(x<=mid) rtn+=query(k<<1,l,mid,x,y);
    	if(y>mid) rtn+=query(k<<1|1,mid+1,r,x,y);
    	return rtn;
    }
    
    int Query(int k,int l,int r,int x,int y) {
    	pushdown(k,l,r);
    	if(x<=l&&r<=y) 
    		return t[k].mx[1];
    	int mid=(l+r)>>1;
    	int rtn=0;
    	if(x<=mid) rtn=max(rtn,Query(k<<1,l,mid,x,y));
    	if(y>mid) rtn=max(rtn,Query(k<<1|1,mid+1,r,x,y));
    	if(x<=mid&&y>mid) 
    		rtn=max(rtn,min(mid-x+1,t[k<<1].R[1])+min(y-mid,t[k<<1|1].L[1]));
    	return rtn;
    }
    
    int main() {
    	n=read();
    	m=read();
    	for(int i=1; i<=n; i++) 
    		x[i]=read();
    	build(1,1,n);
    	for(int i=1,op,a,b;i<=m;i++) {
    		op=read();
    		a=read();a++;
    		b=read();b++;
    		if(op==0)
    			modify(1,1,n,a,b,0);
    		if(op==1) 
    			modify(1,1,n,a,b,1);
    		if(op==2) 
    			modify(1,1,n,a,b,2);
    		if(op==3) 
    			printf("%d
    ",query(1,1,n,a,b));
    		if(op==4) 
    			printf("%d
    ",Query(1,1,n,a,b));
    	}
    	return 0;
    }
    
  • 相关阅读:
    沿着河边走,环城60公里
    与客家土楼的约会(叶剑英故居福建)
    与客家土楼的约会(增城河源)
    与客家土楼的约会(东源龙川)
    C#几个经常犯错误
    C# 获取当前路径方法
    C# Stopwatch与TimeSpan详解
    话说程序员成熟的标志
    C#开源项目介绍
    C#各种加密算法的研究
  • 原文地址:https://www.cnblogs.com/zhuier-xquan/p/11740482.html
Copyright © 2011-2022 走看看