在代碼前先介紹一些我的線段樹風格:
maxn是題目給的最大區間,而節點數要開4倍,確切的來說節點數要開大於maxn的最小2x的兩倍
lson和rson分辨表示結點的左兒子和右兒子,由於每次傳參數的時候都固定是這幾個變量,所以可以用預定於比較方便的表示
以前的寫法是另外開兩個個數組記錄每個結點所表示的區間,其實這個區間不必保存,一邊算一邊傳下去就行,只需要寫函數的時候多兩個參數,結合lson和rson的預定義可以很方便
PushUP(int rt)是把當前結點的信息更新到父結點
PushDown(int rt)是把當前結點的信息更新給兒子結點
rt表示當前子樹的根(root),也就是當前所在的結點
整理這些題目後我覺得線段樹的題目整體上可以分成以下四個部分:
單點更新:最最基礎的線段樹,只更新葉子節點,然後把信息用PushUP(int r)這個函數更新上來
hdu1166 敵兵佈陣http://acm.hdu.edu.cn/showproblem.php?pid=1166
題意:O(-1)
思路:O(-1)
線段樹功能:update:單點增減 query:區間求和
#include <cstdio>
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
const int maxn = 55555;
int sum[maxn<<2];
void PushUP(int rt) {
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
void build(int l,int r,int rt) {
if (l == r) {
scanf("%d",&sum[rt]);
return ;
}
int m = (l + r) >> 1;
build(lson);
build(rson);
PushUP(rt);
}
void update(int p,int add,int l,int r,int rt) {
if (l == r) {
sum[rt] += add;
return ;
}
int m = (l + r) >> 1;
if (p <= m) update(p , add , lson);
else update(p , add , rson);
PushUP(rt);
}
int query(int L,int R,int l,int r,int rt) {
if (L <= l && r <= R) {
return sum[rt];
}
int m = (l + r) >> 1;
int ret = 0;
if (L <= m) ret += query(L , R , lson);
if (R > m) ret += query(L , R , rson);
return ret;
}
int main() {
int T , n;
scanf("%d",&T);
for (int cas = 1 ; cas <= T ; cas ++) {
printf("Case %d:\n",cas);
scanf("%d",&n);
build(1 , n , 1);
char op[10];
while (scanf("%s",op)) {
if (op[0] == 'E') break;
int a , b;
scanf("%d%d",&a,&b);
if (op[0] == 'Q') printf("%d\n",query(a , b , 1 , n , 1));
else if (op[0] == 'S') update(a , -b , 1 , n , 1);
else update(a , b , 1 , n , 1);
}
}
return 0;
}
hdu1754 I Hate It
http://acm.hdu.edu.cn/showproblem.php?pid=1754
題意:O(-1)
思路:O(-1)
線段樹功能:update:單點替換 query:區間最值
#include <cstdio> #include <algorithm> using namespace std; #define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 1 const int maxn = 222222; int MAX[maxn<<2]; void PushUP(int rt) { MAX[rt] = max(MAX[rt<<1] , MAX[rt<<1|1]); } void build(int l,int r,int rt) { if (l == r) { scanf("%d",&MAX[rt]); return ; } int m = (l + r) >> 1; build(lson); build(rson); PushUP(rt); } void update(int p,int sc,int l,int r,int rt) { if (l == r) { MAX[rt] = sc; return ; } int m = (l + r) >> 1; if (p <= m) update(p , sc , lson); else update(p , sc , rson); PushUP(rt); } int query(int L,int R,int l,int r,int rt) { if (L <= l && r <= R) { return MAX[rt]; } int m = (l + r) >> 1; int ret = 0; if (L <= m) ret = max(ret , query(L , R , lson)); if (R > m) ret = max(ret , query(L , R , rson)); return ret; } int main() { int n , m; while (~scanf("%d%d",&n,&m)) { build(1 , n , 1); while (m --) { char op[2]; int a , b; scanf("%s%d%d",op,&a,&b); if (op[0] == 'Q') printf("%d\n",query(a , b , 1 , n , 1)); else update(a , b , 1 , n , 1); } } return 0; }
hdu1394 Minimum Inversion Number
http://acm.hdu.edu.cn/showproblem.php?pid=1394
題意:求Inversion後的最小逆序數
思路:用O(nlogn)複雜度求出最初逆序數後,就可以用O(1)的複雜度分別遞推出其他解
線段樹功能:update:單點增減 query:區間求和
#include <cstdio>
#include <algorithm>
using namespace std;
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
const int maxn = 5555;
int sum[maxn<<2];
void PushUP(int rt) {
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
void build(int l,int r,int rt) {
sum[rt] = 0;
if (l == r) return ;
int m = (l + r) >> 1;
build(lson);
build(rson);
}
void update(int p,int l,int r,int rt) {
if (l == r) {
sum[rt] ++;
return ;
}
int m = (l + r) >> 1;
if (p <= m) update(p , lson);
else update(p , rson);
PushUP(rt);
}
int query(int L,int R,int l,int r,int rt) {
if (L <= l && r <= R) {
return sum[rt];
}
int m = (l + r) >> 1;
int ret = 0;
if (L <= m) ret += query(L , R , lson);
if (R > m) ret += query(L , R , rson);
return ret;
}
int x[maxn];
int main() {
int n;
while (~scanf("%d",&n)) {
build(0 , n - 1 , 1);
int sum = 0;
for (int i = 0 ; i < n ; i ++) {
scanf("%d",&x[i]);
sum += query(x[i] , n - 1 , 0 , n - 1 , 1);
update(x[i] , 0 , n - 1 , 1);
}
int ret = sum;
for (int i = 0 ; i < n ; i ++) {
sum += n - x[i] - x[i] - 1;
ret = min(ret , sum);
}
printf("%d\n",ret);
}
return 0;
}
成段更新
(通常這對初學者來說是一道坎),需要用到延遲標記(或者說懶惰標記),簡單來說就是每次更新的時候不要更新到底,用延遲標記使得更新延遲到下次需要更新or詢問到的時候
hdu1698 Just a Hook
http://acm.hdu.edu.cn/showproblem.php?pid=1698
題意:O(-1)
思路:O(-1)
線段樹功能:update:成段替換 (由於只query一次總區間,所以可以直接輸出1結點的信息
#include <cstdio>
#include <algorithm>
using namespace std;
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
const int maxn = 111111;
int h , w , n;
int col[maxn<<2];
int sum[maxn<<2];
void PushUp(int rt) {
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
void PushDown(int rt,int m) {
if (col[rt]) {
col[rt<<1] = col[rt<<1|1] = col[rt];
sum[rt<<1] = (m - (m >> 1)) * col[rt];
sum[rt<<1|1] = (m >> 1) * col[rt];
col[rt] = 0;
}
}
void build(int l,int r,int rt) {
col[rt] = 0;
sum[rt] = 1;
if (l == r) return ;
int m = (l + r) >> 1;
build(lson);
build(rson);
PushUp(rt);
}
void update(int L,int R,int c,int l,int r,int rt) {
if (L <= l && r <= R) {
col[rt] = c;
sum[rt] = c * (r - l + 1);
return ;
}
PushDown(rt , r - l + 1);
int m = (l + r) >> 1;
if (L <= m) update(L , R , c , lson);
if (R > m) update(L , R , c , rson);
PushUp(rt);
}
int main() {
int T , n , m;
scanf("%d",&T);
for (int cas = 1 ; cas <= T ; cas ++) {
scanf("%d%d",&n,&m);
build(1 , n , 1);
while (m --) {
int a , b , c;
scanf("%d%d%d",&a,&b,&c);
update(a , b , c , 1 , n , 1);
}
printf("Case %d: The total value of the hook is %d.\n",cas , sum[1]);
}
return 0;
}