1. 程式人生 > >【例題】【線段樹】連續區間

【例題】【線段樹】連續區間

1、
NKOJ 2753 區間連續值
時間限制 : 10000 MS 空間限制 : 65536 KB

問題描述
有一數列只有0和1構成,數列中數字個數為為n。
現在有m個形式為x y的提問,詢問區間[x,y]中,最多有多少個連續的1。
對於每個詢問,請你快速做出回答

輸入格式
第一行,兩個整數n和m
第二行,n個空格間隔的數字,表示數列
接下來m行,每行兩個空格間隔的整數x和y,表示一個詢問(x<=y)

輸出格式
m行,每行一個整數,對應詢問的答案

樣例輸入
9 3
1 0 0 0 1 1 1 0 1
1 9
3 6
2 4

樣例輸出
3
2
0

提示


1<=n,m<=100000

#include<iostream>
#include<cstdio>
using namespace std;
const int need=100004;

struct fy{int a,b,fl,fr,v;}t[need*2];
//a,b表邊界;fl從左往右可得到的最大;fr從右往左;;v該段內的最大
int le[need*2],ri[need*2],c[need];
int tot=0,x,y;

int max3(int a,int b,int c)
{
    return max(a,max(b,c));
}

void build(int
x,int y) { int s=++tot; t[s].a=x,t[s].b=y; if(x==y) { t[s].fl=t[s].fr=t[s].mid=t[s].v=c[x]; return ; } le[s]=tot+1;build(x,(x+y)/2); ri[s]=tot+1;build((x+y)/2+1,y); int llen=t[le[s]].b-t[le[s]].a+1,rlen=t[ri[s]].b-t[ri[s]].a+1; t[s].fl=t[le[s]].fl==llen?t[le[s]].fl+t[ri[s]].fl:t[le[s]].fl; t[s].fr=t[ri[s]].fr==rlen?t[ri[s]].fr+t[le[s]].fr:t[ri[s]].fr; //如果左/右兒子全是1,則fl/fr應為左/右兒子長度加上右/左兒子的fl/fr
t[s].v=max3(t[s].v,t[ri[s]].v,t[le[s]].fr+t[ri[s]].fl); //該段內的最大應在左右兒子的最大和中間的最大中選,中間的最大為左兒子右側加上右兒子左側 } int cnt(int s) { if(x>t[s].b||y<t[s].a) return 0; if(x<=t[s].a&&t[s].b<=y) return t[s].v;//如果覆蓋完了直接返回整個區間的最大值 int la,ra,ma=0; la=cnt(le[s]); ra=cnt(ri[s]); //如果沒覆蓋玩應該找左右兒子在區間內的最大值 if(x<=t[le[s]].b&&t[ri[s]].a<=y)//如果所求區間於左右兒子都有交集,就要找中間的最大值 { int temp=t[le[s]].b-x+1; ma+=min(t[le[s]].fr,temp); temp=y-t[ri[s]].a+1; ma+=min(t[ri[s]].fl,temp); } return max3(la,ra,ma);//該段內的最大應在左右兒子的最大和中間的最大中選 } int main() { int n,m;scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&c[i]); build(1,n); for(int i=1;i<=m;i++) { scanf("%d%d",&x,&y); printf("%d\n",cnt(1)); } }

2、
NKOJ 1316 小白逛公園
時間限制 : 20000 MS 空間限制 : 65536 KB

問題描述
小新經常陪小白去公園玩,也就是所謂的遛狗啦…在小新家附近有一條“公園路”,路的一邊從南到北依次排著n個公園,小白早就看花了眼,自己也不清楚該去哪些公園玩了。
一開始,小白就根據公園的風景給每個公園打了分-.-。小新為了省事,每次遛狗的時候都會事先規定一個範圍,小白只可以選擇第a個和第b個公園之間(包括a、b兩個公園)選擇連續的一些公園玩。小白當然希望選出的公園的分數總和儘量高咯。同時,由於一些公園的景觀會有所改變,所以,小白的打分也可能會有一些變化。
那麼,就請你來幫小白選擇公園吧。

輸入格式
第一行,兩個整數N和M,分別表示表示公園的數量和操作(遛狗或者改變打分)總數。
接下來N行,每行一個整數,依次給出小白 開始時對公園的打分。
接下來M行,每行三個整數。第一個整數K,1或2。K=1表示,小新要帶小白出去玩,接下來的兩個整數a和b給出了選擇公園的範圍(1≤a,b≤N);K=2表示,小白改變了對某個公園的打分,接下來的兩個整數p和s,表示小白對第p個公園的打分變成了s(1≤p≤N)。
其中,1≤N≤500 000,1≤M≤100 000,所有打分都是絕對值不超過1000的整數。

輸出格式
小白每出去玩一次,都對應輸出一行,只包含一個整數,表示小白可以選出的公園得分和的最大值。

樣例輸入
5 3
1 2 -3 4 5
1 2 3
2 2 -1
1 2 3

樣例輸出
2
-1

來源 vijos

思路:
該題即為求求任意區間的最大連續和

#include<cstdio>
#include<iostream>
using namespace std;
const int need=500004;
const int inf=1e9;

struct fy{int a,b,fl,fr,fmax,sum;}t[need*2];

int le[need*2],ri[need*2],c[need];
int tot=0,x,y,k,d;

int max3(int a,int b,int c){return max(a,max(b,c));}

void update(int s)
{
    t[s].sum=t[le[s]].sum+t[ri[s]].sum;
    t[s].fl=max(t[le[s]].fl,t[le[s]].sum+t[ri[s]].fl);
    t[s].fr=max(t[ri[s]].fr,t[ri[s]].sum+t[le[s]].fr);
    t[s].fmax=max3(t[le[s]].fmax,t[ri[s]].fmax,t[le[s]].fr+t[ri[s]].fl); 
}

void build(int x,int y)
{
    int s=++tot;
    t[s].a=x,t[s].b=y;
    if(x==y)
    {
        t[s].fl=t[s].fr=t[s].fmax=t[s].sum=c[x];
        return ;
    }
    le[s]=tot+1;build(x,(x+y)>>1);
    ri[s]=tot+1;build((x+y)/2+1,y);
    update(s);
}

void change(int s)
{
    if(t[s].a==t[s].b)
    {
        t[s].fl=t[s].fr=t[s].fmax=t[s].sum=d;
        return ;
    }
    if(t[le[s]].a<=k&&k<=t[le[s]].b) change(le[s]);
    else change(ri[s]);
    update(s);
}
int getr(int s)//查詢s向右在x,y區間內可得到的max,注意x<=s.a<=y恆成立 
{ 
    if(x<=t[s].a&&t[s].b<=y) return t[s].fl;//全覆蓋 
    int mid=(t[s].a+t[s].b)>>1;
    if(y>mid) return max(t[le[s]].fl,t[le[s]].sum+getr(ri[s])); //覆蓋全部s的左兒子和部分右兒子 
    else return getr(le[s]);//覆蓋s的部分左兒子 
}
int getl(int s)//查詢s向左在x,y區間內可得到的max,注意x<=s.b<=y恆成立
{
    if(x<=t[s].a&&t[s].b<=y) return t[s].fr;
    int mid=(t[s].a+t[s].b)>>1;
    if(mid>=x) return max(t[ri[s]].fr,t[ri[s]].sum+getl(le[s])); //全部右,部分左 
    else return getl(ri[s]);//部分右 
}
int solve(int s)
{
    if(x>t[s].b||y<t[s].a) return -inf;
    if(x<=t[s].a&&t[s].b<=y) return t[s].fmax;
    int la=solve(le[s]),ra=solve(ri[s]),ma=-inf;
    if(x<=t[le[s]].b&&t[ri[s]].a<=y)
    {
        ma=getl(le[s])+getr(ri[s]);
    }
    return max3(la,ra,ma);
}

int main()
{
    int n,m;scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) 
    {
        scanf("%d",&c[i]);
        t[i].fl=t[i].fr=t[i].fmax=-inf;
        t[i+n].fl=t[i+n].fr=t[i+n].fmax=-inf;
    }
    build(1,n);
    for(int i=1,e;i<=m;i++)
    {
        scanf("%d",&e);
        if(e==1)
        {
            scanf("%d%d",&x,&y);
            if(x>y) swap(x,y);//可能出現x>y的情況
            printf("%d\n",solve(1));
        }
        else 
        {
            scanf("%d%d",&k,&d);
            change(1);
        }
    }
}