BZOJ 1500 NOI2005 維修數列

2021-07-10 19:32:21 字數 2801 閱讀 6591

輸入的第1 行包含兩個數n 和m(m ≤20 000),n 表示初始時數列中數的個數,m表示要進行的運算元目。

第2行包含n個數字,描述初始時的數列。

以下m行,每行一條命令,格式參見問題描述中的**。

任何時刻數列中最多含有500 000個數,數列中任何乙個數字均在[-1 000, 1 000]內。

插入的數字總數不超過4 000 000個,輸入檔案大小不超過20mbytes。

output

對於輸入資料中的get-sum和max-sum操作,向輸出檔案依次列印結果,每個答案(數字)佔一行。

本題可用splay來維護這個數列,而每次插入操作都將插入的數列建一棵splay然後併入原樹即可。而查詢區間以及各種修改操作則依賴於伸展操作。查詢區間[l,r]時,將節點l-1轉至樹根,將節點r+1轉至根節點的右子節點,然後root->ch[1]->ch[0]就是要操作的區間,將標記打在該節點即可,在旋轉和查詢的時候要注意標記的下傳和節點的更新。

特殊的,要查詢區間的最大連續子串行和:該操作需要對每個節點維護幾個值,該節點及其子樹中的最大字首和,最大字尾和,以及最大子串行和;則在更新的時候該節點的最大子串行和=max(左子樹的最大字尾+該節點的值,右子樹的最大字首+該節點的值,左子樹的最大子串行,右子樹的最大子串行,左子樹最大字尾+節點權值+右子樹最大字首,該節點權值);這樣進行更新,則可以保證該節點維護值的正確性,最大字首與字尾的維護與其相似。注意在更新前要先將該節點及其子節點的標記下傳;

還有乙個問題,那就是怎樣保證這樣做的正確性,我們在修改區間值和翻轉時只是將標記打在了子樹的根節點上,那麼該節點的資訊還是正確的麼?我們在建樹時是遞迴進行的,顯然每個點的資訊都是最新的,並且是從底向上進行更新,正確性顯然可以保證;在我們打翻轉標記的時候,該子樹的最大子串行顯然是不變的(子串行翻轉後相鄰的數字是不變的),最大字首與字尾只是交換了位置而已,最大字首變成了字尾,字尾變成了字首,將兩個值交換即可;在修改值時,將標記打在節點上,當需要用到該節點或其子樹的資訊時,將標記下傳,同時更新節點的資訊,如果修改後的值為正數,則很顯然max_sum=max_pre=max_sur=該子樹節點數x修改後的值,若為負數,則等於該值。所以,只要將標記打在要修改的子樹,在呼叫時更新即可;還有,在每次操作完成後,將該節點到根的路徑更新,保證資訊的正確性;

在操作時還有乙個問題:如果l==1或r==n怎麼辦;顯然節點l-1與r+1是不存在的。於是我們另設兩個節點,分別加在原樹的前面與後面,設成乙個極小的負數,這樣它就不會對其他節點的資訊有影響,因為當查詢區間時,只有在區間內的值才有效,很明顯這兩個新節點是無論如何都不會在區間內的;;而唯一查詢全部節點的操作時最大子串行,由於新節點的值為極小值,所以顯然不會將它們選中。(注意極小值不要太小,不然更新時相加可能會爆int!)

嗯。。就是這樣。

還有注意加個特判,,,如果tot==0直接輸出0或直接return,,資料有毒。。

**如下:

#include#include#include#include#define n 500010

#define inf 1500000

using namespace std;

struct node

}*head,*null,o[n];

queueq;int a[500001],tot,top;

intin()

int check(node *now)

void push(node *now)

node *out(node *fa,int num)

void mark_down(node *now)

if(now->sa)

}void update(node *now)

else

if(now->ch[1]!=null)

else

now->max_sum=max(now->max_sum,now->num);

if(now->ch[0]!=null&&now->ch[1]!=null)

if(now->ch[0]!=null) now->max_sum=max(max(now->max_sum,now->ch[0]->max_sum),now->ch[0]->sur+now->num);

if(now->ch[1]!=null) now->max_sum=max(max(now->max_sum,now->ch[1]->max_sum),now->ch[1]->pre+now->num);

}void rorate(node *now)

void splay(node *now,node *fa) return;

}node *build(int l,int r,node *fa,int d)

node *find(int k) return now;

}void insert()

void del()

void make_same()

void max_sum()

void reverse()

void get_sum()

splay(head=find(point),null);splay(find(point+tot+1),head);

update(head->ch[1]->ch[0]);update(head->ch[1]);update(head);

printf("%d\n",head->ch[1]->ch[0]->sum);

}int main()

}

BZOJ1500 NOI2005 維修數列

description input 輸入檔案的第1行包含兩個數n和m,n表示初始時數列中數的個數,m表示要進行的運算元目。第2行包含n個數字,描述初始時的數列。以下m行,每行一條命令,格式參見問題描述中的 output 對於輸入資料中的get sum和max sum操作,向輸出檔案依次列印結果,每個...

bzoj1500 NOI2005 維修數列

splay鼻祖級的題目?霧。insert 把第pos個數 有哨兵節點 轉到root,把第pos 1個數轉到root的右兒子,然後對c建樹然後把這棵樹插到root右兒子的左兒子處 delete 把第pos個數轉到root,把第pos tot 1個數轉到root右兒子,刪掉root右兒子的左兒子 變成0...

bzoj1500 NOI2005 維修數列

description input 輸入檔案的第1行包含兩個數n和m,n表示初始時數列中數的個數,m表示要進行的運算元目。第2行包含n個數字,描述初始時的數列。以下m行,每行一條命令,格式參見問題描述中的 output 對於輸入資料中的get sum和max sum操作,向輸出檔案依次列印結果,每個...