BZOJ1558 等差數列(線段樹)

2021-08-16 02:47:20 字數 1620 閱讀 9716

題目大意:給出長為n(n<=100000)的序列v,q(q<=100000)次操作,每次對當前序列的[s,t]加上以a為首項b為公差的等差數列,或詢問當前序列[s,t]最少能劃分成多少段等差數列。

題解:神奇的線段樹!

等差數列差分之後值是相同的,便於統計最少劃分數,所以我們可以維護差分陣列。

這樣修改操作就變成s-1和t+1兩個位置的單點加和s~t-1的區間加了。

而關於查詢操作,由於差分數列中x個數對應原序列的x+1個數,所以在合併兩個子區間的時候,注意原序列中對應區間中點的那個數隻屬於左右中的乙個等差數列中,所以左區間最右的差值和右區間最左的差值可以只考慮其中乙個,或者當二者相等時可以跨越合併成乙個等差數列。(表達可能有點不清楚,看**會好一點吧 o…o)

所以可以維護每個區間最左差值 l 和最右的差值 r ,並維護不考慮最左的差值時的最小劃分數 s[2],不考慮最右的差值時的最小劃分數 s[1],最左最右的差值都考慮時的最小劃分數 s[3],最左最右的差值都不考慮時的最小劃分數 s[0],就容易合併了。

總結:1、感覺很多線段樹的題有類似維護保留兩端點、去掉左端點、去掉右端點、去掉兩端點的值來合併區間和計算答案的 2、這種struct的寫法感覺很好用,合併資訊很方便

code(有參考pine大牛的洛谷部落格)

#include

#include

#include

#include

#include

#define n 100005

using

namespace

std;

inline

int read()

while (c<='9'&&c>='0')

return num*f;

}struct data

};struct nodet[n<<2];

int n,q,v[n];

void pushdown(int now)

}void build(int now,int l,int r)

int mid=l+r>>1;

build(now<<1,l,mid); build(now<<1|1,mid+1,r);

t[now].tag=0; t[now].x=t[now<<1].x+t[now<<1|1].x;

}void change(int now,int l,int r,int begin,int end,int key)

pushdown(now); int mid=l+r>>1;

if (begin<=mid) change(now<<1,l,mid,begin,end,key);

if (end>mid) change(now<<1|1,mid+1,r,begin,end,key);

t[now].x=t[now<<1].x+t[now<<1|1].x;

}data query(int now,int l,int r,int begin,int end)

int main()

else

data ret=query(1,1,n-1,s,t-1);

printf("%d\n",ret.s[3]);}}

return

0;}

BZOJ1558 JSOI2009 等差數列

傳送門等差數列的題麼,先差分一下,然後就變成了乙個數列上,求 l,r 區間內連續相同的段數了。很相似的是 sdoi2011 染色這道題,但是由於我們這個線段樹存的是差分後的陣列,所以需要考慮乙個數是否作為乙個分段的頭和尾造成的影響。這個也是可以使用線段樹快速維護的。include using nam...

bzoj 1558 JSOI2009 等差數列

把原陣列變為差分陣列,然後剩下的就十分顯然了 區間查詢用線段樹維護 修改操作就是區間加法和兩個單點修改 乙個等差數列實際上就是 開頭乙個數字 數值相等的一段 唯一的難點在於討論這個開頭的數字的去向 所以我們可以先不把左右兩端點列入考慮物件,然後在合併時再討論去向,綜上需要維護的東西有 1.區間的左右...

45 等差數列

45 等差數列 問題描述 乙個等差數列是乙個能表示成a,a b,a 2b,a nb n 0,1,2,3,在這個問題中a是乙個非負的整數,b是正整數。寫乙個程式來找出在雙平方數集合s中長度為n的等差數列。雙平方數集合是所有能表示成p2 q2的數的集合。輸入說明 第一行 n 3 n 25 要找的等差數列...