zoukankan      html  css  js  c++  java
  • COCI2010/2011 踢踏舞 线段树区间子段和

    无题目传送门

    大致题意:

    有一初始为(0),长度为(n)的序列。(q)次操作反转单点(01互换)。每次操作后求最长的(01)间隔排列的子段长度。

    (1leq n,q leq 200000)


    Solution

    直接求(01)的序列长是比较困难的(指蒟蒻用珂朵莉疯狂TLE),所以考虑转化。

    因为是(01)间隔的,所以把它们每相邻两个异或起来就会得到一个全(1)且长度小(1)的序列。

    比如(010100)就会变成(11110)

    由于是单点修改,所以每次仅会影响对应异或序列中的前后元素。

    所以可以方便地把问题转化为,维护一个长度为(n-1)的序列,每次对(x)的操作改为对(x)(x+1)的取反(如果不为(1)(n))。这在原序列中表示的是((x,x-1))((x,x+1))的异或值。

    然后最后的问题就是解决在这样一个序列中求最长的连续(1)的个数了。

    由于是动态的,维护序列自然想到线段树。最长连续(1)的个数可以换一个思维,也就是不含0的最大子段和

    那么怎么做到不含0呢?只需要让我们求最大子段和取不到0就行了。那么把原有的0都换成极小值,再用线段树维护一个区间子段和就可以了。

    简单说下求区间子段和。

    一个线段树节点维护四个信息:(l,r,v,s),分别表示以左端点开头的最大子段和,以右端点结束的最大子段和,节点内最大子段和,节点内权值和

    每次都是单点修改,那么修改后的节点(k)四个参数分别应该为0,0,0,-INF(修改为负值)或1,1,1,1(修改为正值)。

    核心:

    t[k].s=t[ls].s+t[rs].s;
    t[k].l=max(t[ls].l,t[ls].s+t[rs].l);
    t[k].r=max(t[rs].r,t[rs].s+t[ls].r);
    t[k].v=max(max(t[ls].v,t[rs].v),t[ls].r+t[rs].l);
    

    从左端点开头的最大子段和,要么是他左儿子的最大子段和,要么是他左儿子的权值和再加上右儿子从左端开始的最大子段和。这样是两种保证子段连续的情况。右端点结尾的同理。

    整体的最大子段和要么是左/右儿子的最大子段和,要不是左右儿子各出自己的右部分和左部分拼在一起。

    于是就完了


    code:

    #include<bits/stdc++.h>
    #define int long long
    #define N 200020
    #define INF 0x3f3f3f3f
    #define ls (k<<1)
    #define rs (k<<1|1)
    using namespace std;
    struct node
    {
    	int l,r,v,s;
    }t[N<<2];
    int val[N];
    void work(int k,int l,int r,int p,int c)
    {
    	if(l==r)
    	{
    		if(c==1)t[k]=(node){1,1,1,1};
    		else t[k]=(node){0,0,0,c};
    		return;
    	}
    	int m=l+r>>1;
    	if(p<=m)work(ls,l,m,p,c);
    	else work(rs,m+1,r,p,c);
    	t[k].s=t[ls].s+t[rs].s;
    	t[k].l=max(t[ls].l,t[ls].s+t[rs].l);
    	t[k].r=max(t[rs].r,t[rs].s+t[ls].r);
    	t[k].v=max(max(t[ls].v,t[rs].v),t[ls].r+t[rs].l);
    }
    void build(int l,int r,int k)
    {
    	if(l==r)
    	{
    		t[k].s=-INF;
    		return;
    	}
    	int m=l+r>>1;
    	build(l,m,ls);
    	build(m+1,r,rs);
    	t[k].s=t[ls].s+t[rs].s;
    }
    signed main()
    {
    	int n,m,x;
    	scanf("%lld%lld",&n,&m);
    	build(1,n,1);
    	while(m--)
    	{
    		scanf("%lld",&x);
    		val[x]=(!val[x]);
    		if(x!=1)work(1,1,n,x  ,val[x]^val[x-1]?1:-INF);
    		if(x!=n)work(1,1,n,x+1,val[x]^val[x+1]?1:-INF);
    		printf("%lld
    ",t[1].v+1);
    	}
    }
  • 相关阅读:
    蛙蛙推荐:改进了一个DBAccess类,顺便说说啥是线程安全
    蛙蛙推荐:SQLServer优化资料整理
    快速配置Eclipse3.1.1+Tomcat5.0+Lomboz dodo
    建立asp.net应用程序提示:无法与服务器建立连接 dodo
    tomcat连接sqlserver配置 dodo
    如何让DNN页面中的html控件中的文本项显示为中文? dodo
    DropDownList 不能有多个项被选定 dodo
    转换DATAREADER为 dataset dodo
    使用ComponentArt.WebUI.for.Asp.net.3.0的TreeView控件 dodo
    ASP.NET的WEB项目不能添加WEB用户控件的解决 dodo
  • 原文地址:https://www.cnblogs.com/moyujiang/p/13752081.html
Copyright © 2011-2022 走看看