線段樹經典題目(一定要做完)
http://www.notonlysuccess.com/?p=59
這幾天陸陸續續更新了下邊幾道我所能找到得具有一些代表性的線段樹題目
從最最簡單的區間求和到對區間的各種操作都包涵在這些題目裡了
相信對一些準備學習線段樹的人有一定得幫助
突然發現自己對資料結構的題目非常有感覺,所以在刷下邊的題的同時也生出靈感出了好幾道線段樹題目
等比賽結束後也會陸續加進裡邊
快半年過去程式碼風格也有很大的改變,感覺以前寫的程式碼很不規範,用自己在預定義中定義的一些函式,但後來感覺作用不是很大,所以又刪去了,所以現在看程式碼可能找不到以前我定義的一些函式,本來想更新一下的,無奈這些函式用的太多,改之太過麻煩,所以在此處申明一下
#define LL(x) ((x)<<1)
#define RR(x) ((x)<<1|1)
#define FF(i,n) for(int i = 0 ; i < n ; i ++)
若還有不清楚的地方,只管提出來便是,我一定一一改正
1.hdu1166 敵兵佈陣
更新節點,區間求和。一次AC,做的時候也沒遇見什麼問題!
#include <iostream>
#include <string>
using namespace std;
#define MAX_N 50000
string str;
int sum; //記錄總兵數
int num[MAX_N+1]={0}; //記錄各個兵營的兵數
typedef struct node
{
int left;
int right;
int data;
node* lchild;
node* rchild;
node()
{
left = right = data = 0;
}
}Tree;
Tree* CreateTree(int a,int b)
{
Tree* r;
r = (Tree*)malloc(sizeof(Tree));
r->left = a;
r->right = b;
if(a == b)
{
r->data = num[a];
r->lchild = r->rchild = NULL;
}
else
{
int mid = (a+b)>>1;
r->lchild = CreateTree(a,mid);
r->rchild = CreateTree(mid+1,b);
r->data = r->lchild->data + r->rchild->data;
}
return r;
}
void insert(Tree* r,int a,int b)
{
if(r->left == a && r->right == a)
{
r->data += b;
return;
}
int mid = (r->left + r->right)>>1;
if(a <= mid)
{
insert(r->lchild,a,b);
}
else
{
insert(r->rchild,a,b);
}
r->data += b;
}
void find(Tree* r,int a,int b)
{
if(r->left == a && r->right == b)
{
sum += r->data;
return;
}
int mid = (r->left + r->right)>>1;
if(b<=mid)
{
find(r->lchild,a,b);
}
else if(a>mid)
{
find(r->rchild,a,b);
}
else
{
find(r->lchild,a,mid);
find(r->rchild,mid+1,b);
}
}
int main()
{
int t,n,x,y;
int i;
int ca = 0;
scanf("%d",&t);
while(t--)
{
printf("Case %d:/n",++ca);
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d",&num[i]);
}
Tree* T;
T = CreateTree(1,n);
while(cin>>str)
{
if(str == "Query")
{
sum = 0;
scanf("%d%d",&x,&y);
find(T,x,y);
printf("%d/n",sum);
}
else if(str == "Add")
{
scanf("%d%d",&x,&y);
insert(T,x,y);
}
else if(str == "Sub")
{
scanf("%d%d",&x,&y);
insert(T,x,-y);
}
else
{
break;
}
}
}
return 0;
}
.
2.hdu1754 I Hate It
更新節點,區間最值。一開始用連結串列建樹,發現MLE了,就改成了陣列建樹,過!
#include <iostream>
using namespace std;
#define MAX_N 200000
int big;
typedef struct node
{
int left;
int right;
int data;
int maxx;
}node;
node stu[MAX_N*4];
//int num[MAX_N+1];
int Max(int a,int b)
{
return a>b?a:b;
}
void CreateTree(int ii,int a,int b)
{
stu[ii].left = a;
stu[ii].right = b;
stu[ii].maxx = 0;
stu[ii].data = 0;
if(a == b)
{
return;
}
else
{
int mid = (a+b)>>1;
CreateTree(ii*2,a,mid);
CreateTree(ii*2+1,mid+1,b);
}
}
void updata(int ii,int a,int b)
{
if(stu[ii].left == a && stu[ii].right == a)
{
stu[ii].data = b;
stu[ii].maxx = b;
}
else
{
int mid = (stu[ii].left+stu[ii].right)>>1;
if(a <= mid)
{
updata(ii*2,a,b);
}
else
{
updata(ii*2+1,a,b);
}
if(b > stu[ii].maxx)
stu[ii].maxx = b;
}
}
void find(int ii,int a,int b)
{
if(stu[ii].left == a && stu[ii].right == b)
{
//printf("%d/n",stu[ii].maxx);
if(stu[ii].maxx > big)
big = stu[ii].maxx;
}
else
{
int mid = (stu[ii].left + stu[ii].right)>>1;
if(b <= mid)
{
find(ii*2,a,b);
}
else if(a > mid)
{
find(ii*2+1,a,b);
}
else
{
find(ii*2,a,mid);
find(ii*2+1,mid+1,b);
}
}
}
int main()
{
int n,m,num;
int i;
while(scanf("%d%d",&n,&m)!=EOF)
{
CreateTree(1,1,n);
for(i=1;i<=n;i++)
{
scanf("%d",&num);
updata(1,i,num);
}
char c;
int x1,x2;
while(m--)
{
scanf("%*c%c",&c);
scanf("%d%d",&x1,&x2);
if(c == 'Q')
{
big = 0x80000000;
find(1,x1,x2);
printf("%d/n",big);
}
else
{
updata(1,x1,x2);
}
}
}
return 0;
}
.
3.hdu1698 Just a Hook
成段更新,總區間求和.也不是很難,注意遞迴時一層一層的遞迴下去。寫的時候中間忽略了一點,造成錯了好幾次!
#include <iostream>
using namespace std;
#define MAX_N 100000
struct node
{
int left;
int right;
int data;
int sum;
};
node hook[4*MAX_N];
void CreateTree(int ii,int a,int b)
{
hook[ii].left = a;
hook[ii].right = b;
if(a == b)
{
hook[ii].data = 1;
hook[ii].sum = 1;
}
else
{
int mid = (a+b)>>1;
CreateTree(ii*2,a,mid);
CreateTree(ii*2+1,mid+1,b);
hook[ii].data = 0;
hook[ii].sum = (hook[ii*2].sum + hook[ii*2+1].sum);
}
}
void updata(int ii,int a,int b,int c)
{
if(hook[ii].left == a && hook[ii].right == b)
{
hook[ii].data = c;
hook[ii].sum = (b-a+1)*c;
}
else
{
if(hook[ii].data > 0)
{
hook[ii*2].data = hook[ii].data;
hook[ii*2].sum = (hook[ii*2].right - hook[ii*2].left+1)*hook[ii*2].data;
hook[ii*2+1].data = hook[ii].data;
hook[ii*2+1].sum = (hook[ii*2+1].right - hook[ii*2+1].left+1)*hook[ii*2+1].data;
}
hook[ii].data = 0; //寫的時候這個丟掉了。一直錯
int mid = (hook[ii].left + hook[ii].right)>>1;
if(b <= mid)
{
updata(ii*2,a,b,c);
}
else if(a > mid)
{
updata(ii*2+1,a,b,c);
}
else
{
updata(ii*2,a,mid,c);
updata(ii*2+1,mid+1,b,c);
}
hook[ii].sum = hook[ii*2].sum + hook[ii*2+1].sum;
}
}
int main()
{
int t,n,m,x1,x2,x3;
int i;
scanf("%d",&t);
for(i=1;i<=t;i++)
{
scanf("%d",&n);
CreateTree(1,1,n);
scanf("%d",&m);
while(m--)
{
scanf("%d%d%d",&x1,&x2,&x3);
updata(1,x1,x2,x3);
}
printf("Case %d: The total value of the hook is %d./n",i,hook[1].sum);
}
return 0;
}
4.hdu1394 Minimum Inversion Number
更新節點,區間求和(實現nlog(n)的逆序數方法,和樹狀陣列比起來實在是有點雞肋)
.
5.hdu1779 9-Problem C暫不公開
成段更新,區間最值
.
6.pku2777 Count Color
成段更新,區間統計,位運算加速(我總把query裡的傳遞給子節點的步驟忘了)。錯了蠻多次的,一開始是RE,應該是中間跑的時候迴圈有問題,後來的話WA,find函式中存在問題!
#include <iostream>
#include <algorithm>
using namespace std;
#define MAX_N 100000
int t;
int num[35];
typedef struct node
{
int left;
int right;
int data;
node* lchild;
node* rchild;
}Tree;
Tree* CreateTree(int a,int b)
{
Tree* r;
r=(Tree*)malloc(sizeof(Tree));
r->left = a;
r->right = b;
r->data = 1;
if(a+1==b)
r->lchild=r->rchild=NULL;
else
{
int mid = (a+b)>>1;
r->lchild = CreateTree(a,mid);
r->rchild = CreateTree(mid,b);
}
return r;
}
void search(Tree* r,int a,int b,int c)
{
//if(r->data == c)
// return;
if(r==NULL)
return ;
if(r->left == a && r->right == b)
{
r->data = c;
}
else
{
if(r->data > 0)
{
r->lchild->data=r->data;
r->rchild->data=r->data;
}
r->data = 0;
int mid = (r->left + r->right)>>1;
if(a >= mid)
{
search(r->rchild,a,b,c);
}
else if(b <= mid)
{
search(r->lchild,a,b,c);
}
else
{
search(r->lchild,a,mid,c);
search(r->rchild,mid,b,c);
}
//if(r->lchild->data == r->rchild->data)
// {
// r->data = r->lchild->data;
// }
}
}
void find(Tree* r,int a,int b)
{
/*if(r->left == a && r->right == b)
{
if(r->data > 0)
{
//num[r->data][0]++;
if(num[r->data][0] == 0)
{
num[r->data][0]++;
num[r->data][1] = r->right;
}
else
{
if(num[r->data][1] == r->left)
num[r->data][1] = r->right;
else
{
num[r->data][0]++;
num[r->data][1] = r->right;
}
}
}
else if(r->data == 0)
{
int mid = (a+b)>>1;
find(r->lchild,a,mid);
find(r->rchild,mid,b);
}
return;
}*/
if(r==NULL)
return ;
if(r->data > 0)
{
num[r->data]++;
return;
}
int mid = (r->left + r->right)>>1;
if(a >= mid)
{
find(r->rchild,a,b);
}
else if(b <= mid)
{
find(r->lchild,a,b);
}
else
{
find(r->lchild,a,mid);
find(r->rchild,mid,b);
}
}
int Sum()
{
int i;
int sum = 0;
for(i=1;i<=t;i++)
{
if(num[i] != 0)
sum++;
}
return sum;
}
int main()
{
int l,m;
while(scanf("%d%d%d",&l,&t,&m)!=EOF)
{
Tree* T;
T = CreateTree(1,l+1);
while(m--)
{
int x1,x2,color;
char c;
cin>>c;
if(c == 'C')
{
scanf("%d%d%d",&x1,&x2,&color);
if(x1>x2)
swap(x1,x2);
search(T,x1,x2+1,color);
}
else
{
memset(num,0,sizeof(num));
scanf("%d%d",&x1,&x2);
if(x1>x2)
swap(x1,x2);
find(T,x1,x2+1);
printf("%d/n",Sum());
}
}
}
return 0;
}
7.pku3468 A Simple Problem with Integers
成段更新,區間求和(中間乘法會超int..糾結了)
這題int因為一開始就注意,沒出什麼問題。但錯了很多次,在成段更新的時候不能更新到點,這樣肯定對超時的。這題是要加一個增量,表示該節點增加了多少。現在也不是很明白。線段樹覺的重要的就是node中那些個域!
#include <iostream>
using namespace std;
#define MAX_N 100000
__int64 sum = 0;
__int64 num[MAX_N+1];
struct node
{
int left;
int right;
__int64 count; //表示該節點的總個數
__int64 data; //表示增量的那個數
};
node N[4*MAX_N];
void CreateTree(int ini,int a,int b)
{
N[ini].left = a;
N[ini].right = b;
N[ini].data = 0;
if(a == b)
{
N[ini].count= num[a];
}
else
{
int mid = (a+b) >> 1;
if(b <= mid)
CreateTree(ini*2,a,b);
else if(a > mid)
CreateTree(ini*2+1,a,b);
else
{
CreateTree(ini*2,a,mid);
CreateTree(ini*2+1,mid+1,b);
}
N[ini].count= N[ini*2].count+ N[ini*2+1].count;
}
}
void insert(int ini,int a,int b,int c)
{
if(N[ini].left == a && N[ini].right == b)
{
N[ini].data += c;
//N[ini].count+= (b-a+1) * c;
}
else
{
int mid = (N[ini].left + N[ini].right) >> 1;
if(b <= mid)
insert(ini*2,a,b,c);
else if(a > mid)
insert(ini*2+1,a,b,c);
else
{
insert(ini*2,a,mid,c);
insert(ini*2+1,mid+1,b,c);
}
N[ini].count = (N[ini*2].count + (N[ini*2].right - N[ini*2].left + 1) * N[ini*2].data)
+ (N[ini*2+1].count + (N[ini*2+1].right - N[ini*2+1].left + 1) * N[ini*2+1].data);
}
}
void find(int ini,int a,int b)
{
if(N[ini].left == a && N[ini].right == b)
{
sum += N[ini].count + N[ini].data*(b-a+1);
}
else
{
N[ini*2].data += N[ini].data;
N[ini*2+1].data += N[ini].data;
N[ini].count += N[ini].data * (N[ini].right - N[ini].left + 1);
N[ini].data = 0;
int mid = (N[ini].left + N[ini].right) >> 1;
if(b <= mid)
find(ini*2,a,b);
else if(a > mid)
find(ini*2+1,a,b);
else
{
find(ini*2,a,mid);
find(ini*2+1,mid+1,b);
}
}
}
void Init()
{
int n,m,x1,x2,x3;
char c;
int i;
while(scanf("%d%d",&n,&m)!=EOF)
{
for(i=1;i<=n;i++)
scanf("%I64d",&num[i]);
CreateTree(1,1,n);
while(m--)
{
scanf("%*c%c",&c);
if(c == 'Q')
{
sum = 0;
scanf("%d%d",&x1,&x2);
find(1,x1,x2);
printf("%I64d/n",sum);
}
else
{
scanf("%d%d%d",&x1,&x2,&x3);
insert(1,x1,x2,x3);
}
}
}
}
int main()
{
Init();
return 0;
}
.8.pku2528 Mayor’s posters
成段更新,區間統計(離散化)
一個很噁心的題目,昨天晚上看了題意,上網瞭解了一下離散化。離散化就是為了縮短線段的範圍,如兩個線段(1,6)和(4,9),離散化一下就是1->1,6->3,4->2,9->4,那麼離散後的線段就是(1,3)和(2,4),把線段長度從(1,9)縮短到了(1,4),這種離散化是很實用的。離散的過程就是先把線段的座標儲存下來(1,6,4,9),再排序(1,4,6,9),之後對應(1->1,4->2,6->3,9->4),再根據這個對應修改原先的線段就好了。
寫的時候很噁心,一直re,很久以後試試看的態度改了MAX_N,從10005改成30005就行了。應該是用來排序的那個陣列開小了
#include <iostream>
#include <algorithm>
using namespace std;
#define MAX_N 30005 //一開始開10005 就RE了
int n; //線段個數
int num[10005];
int pos[10005][2];
bool vis[10000005];
int hash[10000005];
int set[8*MAX_N];
struct node
{
int left;
int right;
int data;
};
node T[8*MAX_N];
void CreateTree(int ini,int a, int b)
{
T[ini].left = a;
T[ini].right = b;
T[ini].data = -1;
if(a >= b)
return;
int mid = (a+b)>>1;
CreateTree(ini*2,a,mid);
CreateTree(ini*2+1,mid+1,b);
}
void insert(int ini,int a,int b,int color)
{
if(T[ini].left == a && T[ini].right == b)
{
T[ini].data = color;
//return;
}
else
{
if(T[ini].data > 0 && T[ini].data != color)
{
T[ini*2].data = T[ini].data;
T[ini*2+1].data = T[ini].data;
T[ini].data = 0;
}
else if(T[ini].data == -1)
{
T[ini].data = 0;
}
int mid = (T[ini].left+T[ini].right)>>1;
if(b <= mid)
insert(ini*2,a,b,color);
else if(a > mid)
insert(ini*2+1,a,b,color);
else
{
insert(ini*2,a,mid,color);
insert(ini*2+1,mid+1,b,color);
}
}
}
void find(int ini)
{
if(T[ini].left == T[ini].right)
{
if(T[ini].data > 0)
num[T[ini].data]++;
return;
}
if(T[ini].data == -1)
return;
else if(T[ini].data > 0)
{
num[T[ini].data]++;
return;
}
else if(T[ini].data == 0)
{
find(ini*2);
find(ini*2+1);
}
}
int cmp(int a,int b)
{
return a<b;
}
void Init()
{
int i;
memset(vis,0,sizeof(vis));
int j = 0;
scanf("%d",&n);
CreateTree(1,1,8*n);
for(i=0;i<n;i++) //離散化
{
scanf("%d%d",&pos[i][0],&pos[i][1]);
if(vis[pos[i][0]] == 0)
{
set[j++] = pos[i][0];
vis[pos[i][0]] = 1;
}
if(vis[pos[i][1]] == 0)
{
set[j++] = pos[i][1];
vis[pos[i][1]] = 1;
}
}
sort(set,set+j,cmp);
for(i=0;i<=j;i++)
{
hash[set[i]] = i+1;
}
for(i=0;i<n;i++)
{
pos[i][0] = hash[pos[i][0]];
pos[i][1] = hash[pos[i][1]];
insert(1,pos[i][0],pos[i][1],i+1);
}
}
int main()
{
int i,t;
scanf("%d",&t);
while(t--)
{
memset(num,0,sizeof(num));
int sum = 0;
Init();
find(1);
for(i=1;i<=n;i++)
{
if(num[i] > 0)
sum++;
}
printf("%d/n",sum);
}
return 0;
}
.
9.hdu2795 Billboard
更新節點,詢問特殊(暑假的時候不會線段樹被這水題狂虐)
一開始不能理解,不會建樹,一直以為是用10^9,實際上只要20000*4就行了。而data表示最大值。想通這個就好過了。
#include <iostream>
using namespace std;
#define MAX_N 200000
int h,w,n;
struct node
{
int left;
int right;
int data; //表示這一段中最大的數
};
node T[4*MAX_N];
int Max(int a,int b)
{
return a>b?a:b;
}
void CreateTree(int ini,int a,int b)
{
T[ini].left = a;
T[ini].right = b;
T[ini].data = w; //一開始最大都是寬度
if(a == b)
{
return;
}
else
{
int mid = (a + b) >> 1;
CreateTree(ini*2,a,mid);
CreateTree(ini*2+1,mid+1,b);
}
}
void insert(int ini,int a)
{
if(T[ini].left == T[ini].right)
{
printf("%d/n",T[ini].left);
T[ini].data -= a;
}
else
{
if(T[ini*2].data >= a)
{
insert(ini*2,a);
}
else
{
insert(ini*2+1,a);
}
T[ini].data = Max(T[ini*2].data,T[ini*2+1].data);
}
}
int main()
{
int i,num;
while(scanf("%d%d%d",&h,&w,&n)!=EOF)
{
if(h < n)
CreateTree(1,1,h);
else
CreateTree(1,1,n);
//建樹按照min(h,n)來建,這樣就保證最大時n。
for(i=0;i<n;i++)
{
scanf("%d",&num);
if(num > T[1].data)
{
printf("-1/n");
}
else
insert(1,num);
}
}
return 0;
}
.
10.pku3667 Hotel
成段更新,尋找空間(經典型別,求一塊滿足條件的最左邊的空間)
.
11.hdu1540 Tunnel Warfare
更新節點,詢問節點所在區間(同上一道Hotel一樣型別的題目)
.
12.hdu2871 Memory Control
hotel變形題目,三個都函式一樣(vector忘記清空檢查了好久)
.
13.hdu3016 Man Down
成段更新,單點查詢(簡單線段樹+簡單DP)
.
14.hdu1542 Atlantis
矩形面積並,掃描線法(發現我們HDU的隊員的矩形面積交模板基本都是在最壞情況下更新到底,寧波慘痛的教訓啊..)
.
15.hdu1255 覆蓋的面積
同上,掃描線法,我多加了一個係數csum,來統計覆蓋兩次的長度(156MS,第一次做線段樹排到第一,紀念下)
.
16.hdu1828 Picture
掃描線,同面積統計,加了一個num_Seg統計一個掃描區域裡的邊
.
17.pku1436 Horizontally Visible Segments
成段更新,成段詢問(染色問題,座標*2後很簡單,最後統計用暴力- -)
.
18.pku3225 Help with Intervals
成段更新,總詢問區間(有個異或操作比較新穎)
.
19.pku2482 Stars in Your Window
成段更新,區間最值 + 掃描線(轉化成區間最值有點巧妙,資料太TMD的噁心了,中間二分的地方會int溢位,檢查了半個小時)
.
20.pku2828 Buy Tickets
思維很巧妙,倒過來做的話就能確定此人所在的位置
.
21.pku2464 Brownie Points II
更新節點,區間求和 + 掃描線(很好玩的一道題目,用兩個線段樹沿著掃描線更新,然後按”自己最多,對方最少”的方案一路統計)
(因為兩棵樹,我就直接寫成類了)
.
22.pku3145 Harmony Forever
查詢一個區間內最左邊的數,詢問的val大的話用線段樹,小的話線性掃描,很囧的題目
.
23.pku2886 Who Gets the Most Candies?
尋找區間中的左數第N個數,約瑟夫環(學到了反素數表,可以不用把所有數字預處理出來了)
.
24.pku2991 Crane
記錄了線段的兩端點以及轉過的角度,成段的轉,超有意思的題目,做了之後會對線段樹理解更深刻
(wy教主推薦的,不過我的程式碼寫的太搓了..沒啥學習的價值)
.
25.hdu1823 Luck and Love
二維線段樹,沒啥意思,程式碼量大了一倍而已,題目和運用範圍都沒一維的廣,隨便找了道題目練習下就好
相關文章
- js經典題目JS
- 一些線段樹典(求求了區域賽遇到線段樹不要被卡了)
- 部分JS經典題目解析JS
- 山海經:線段樹維護最大子段和
- 勢能線段樹專題
- 洛谷題單指南-線段樹-P3373 【模板】線段樹 2
- 線段樹也能是 Trie 樹 題解
- 線段樹
- 線~段~樹
- hdu 1698 線段樹 一段更新染色
- 經典題目螺旋方陣的詳解
- 線段樹分治略解&雜題解析
- 01 線段樹
- 線段樹--RMQMQ
- 【模版】線段樹
- 線段樹模板
- 線段樹 hate it
- LeetCode經典題-篇一LeetCode
- 作業系統——程序同步互斥經典題目作業系統
- 2019年c語言經典面試題目C語言面試題
- 前端面試經典題目合集(HTML+CSS)前端面試HTMLCSS
- ut.cpp 最大線段並減線段交 [線段樹]
- 暫存一下線段樹模板
- 李超線段樹
- 線段樹進階
- 權值線段樹
- 線段樹筆記筆記
- Segment Tree(線段樹)
- 線段樹入門
- 【Leetcode每日一題】327. 區間和的個數(線段樹/樹狀陣列)LeetCode每日一題陣列
- CF EDU165-E-序列問題,線段樹
- python階段題目Python
- 懶標記線段樹
- 可持久化線段樹持久化
- 線段樹(超詳解)
- 線段樹 transformation——hdu 4578ORM
- 深入理解線段樹
- 線段樹簡單思路