1. 程式人生 > >poj 3468 A Simple Problem with Integers(原來是一道簡單的線段樹區間修改用來練練splay)

poj 3468 A Simple Problem with Integers(原來是一道簡單的線段樹區間修改用來練練splay)

long 兩個 可能 style push ios stream 區間 pan

題目鏈接:http://poj.org/problem?id=3468

題解:splay功能比線段樹強大當然代價就是有些操作比線段樹慢,這題用splay實現的比線段樹慢上一倍。線段樹用lazy標記差不多要2s用splay要4s。可以用splay來實現線段樹的區間操作更深層次的了解一下splay算是入個門。

#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <cstdio>
using namespace
std; const int M = 1e5 + 10; typedef long long ll; int pre[M] , ch[M][2] , size[M] , root , tot;//分別表示父節點,左右兒子,大小,根節點,總共的節點數。 int key[M];//當前節點對應的權值 int add[M];//類似懶惰標記 ll sum[M];//當前節點包括他以下的節點權值的總和可以理解為子樹權值之和加上這個節點的權值 int a[M]; int n , q; void NewNode(int &r , int fa , int k) { r = ++tot; pre[r]
= fa; size[r] = 1; key[r] = k; add[r] = 0; sum[r] = 0; ch[r][0] = ch[r][0] = 0; }//標準的初始化節點 void update(int r , int ad) { if(r == 0) return; add[r] += ad; key[r] += ad; sum[r] += (ll)ad * size[r]; }//節點更新 void push_up(int r) { size[r] = size[ch[r][0]] + size[ch[r][1
]] + 1; sum[r] = sum[ch[r][0]] + sum[ch[r][1]] + key[r]; } void push_down(int r) { if(add[r]) { update(ch[r][0] , add[r]); update(ch[r][1] , add[r]); add[r] = 0; } }//一系列類似線段樹的操作。 void Rotate(int x , int kind) { int y = pre[x]; push_down(y); push_down(x); ch[y][!kind] = ch[x][kind]; pre[ch[x][kind]] = y; if(pre[y]) ch[pre[y]][ch[pre[y]][1] == y] = x; pre[x] = pre[y]; ch[x][kind] = y; pre[y] = x; push_up(y); } void Splay(int r , int goal) { push_down(r); while(pre[r] != goal) { if(pre[pre[r]] == goal) Rotate(r , ch[pre[r]][0] == r); else { int y = pre[r]; int kind = (ch[pre[y]][0] == y); if(ch[y][kind] == y) { Rotate(r , !kind); Rotate(r , kind); } else { Rotate(y , kind); Rotate(r , kind); } } } push_up(r); if(goal == 0) root = r; }//一系列標準的splay的操作 void build(int &x , int l , int r , int fa) { if(l > r) return ; int mid = (l + r) >> 1; NewNode(x , fa , a[mid]); build(ch[x][0] , l , mid - 1 , x); build(ch[x][1] , mid + 1 , r , x); push_up(x); } void init() { root = 0 , tot = 0; ch[root][0] = ch[root][1] = pre[root] = size[root] = add[root] = sum[root] = key[root] = 0; NewNode(root , 0 , -1); NewNode(ch[root][1] , root , -1); build(ch[ch[root][1]][0] , 1 , n , ch[root][1]); push_up(root); push_up(ch[root][1]); }//這裏之所以要優先建兩個點和後面的更新有關 int get_kth(int r , int k) { push_down(r); int t = size[ch[r][0]] + 1; if(t == k) return r; else if(t > k) return get_kth(ch[r][0] , k); else return get_kth(ch[r][1] , k - t); }//獲得第幾大的數 void ADD(int l , int r , int ad) { Splay(get_kth(root , l) , 0); Splay(get_kth(root , r + 2) , root); update(ch[ch[root][1]][0] , ad); push_up(ch[root][1]); push_up(root); }//這裏按照常理應該是將第l-1個節點移到根然後再將r+1的節點移到根的右兒子那麽(l~r)就是r+1節點的左兒子的sum值由於之前加了兩個節點所以變到了l~r+2,畢竟l-1可能為0就是就是根節點處理起來可能會有些不便。當然無視也行,按照個人喜好來就行。 long long query(int l , int r) { Splay(get_kth(root , l) , 0); Splay(get_kth(root , r + 2), root); return sum[ch[ch[root][1]][0]]; }//區間查詢同理 int main() { while(scanf("%d%d" , &n , &q) == 2) { for(int i = 1 ; i <= n ; i++) scanf("%d" , &a[i]); init(); while(q--) { char c[10]; scanf("%s" , c); if(c[0] == Q) { int l , r; scanf("%d%d" , &l , &r); printf("%lld\n" , query(l , r)); } else { int l , r , x; scanf("%d%d%d" , &l , &r , &x); ADD(l , r , x); } } } return 0; }

poj 3468 A Simple Problem with Integers(原來是一道簡單的線段樹區間修改用來練練splay)