线段树

题目是从傻崽博客里拉来的,自己写的代码。

1.hdu1166 敌兵布阵
更新节点,区间求和。一次AC,做的时候也没遇见什么问题!

View Code
#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了,就改成了数组建树,过!

View Code
#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
成段更新,总区间求和.也不是很难,注意递归时一层一层的递归下去。写的时候中间忽略了一点,造成错了好几次!

View Code
#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函数中存在问题!

View Code
#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中那些个域!

View Code
#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就行了。应该是用来排序的那个数组开小了

View Code
#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表示最大值。想通这个就好过了。

View Code
#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
二维线段树,没啥意思,代码量大了一倍而已,题目和运用范围都没一维的广,随便找了道题目练习下就好

原文地址:https://www.cnblogs.com/silencExplode/p/1968524.html