题目是从傻崽博客里拉来的,自己写的代码。
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 ;
}
#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 ;
}
.
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 ;
}
这题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 ;
}
一个很恶心的题目,昨天晚上看了题意,上网了解了一下离散化。离散化就是为了缩 短线段的范围,如两个线段(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 ;
}
一开始不能理解,不会建树,一直以为是用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 ;
}
.
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
二维线段树,没啥意思,代码量大了一倍而已,题目和运用范围都没一维的广,随便找了道题目练习下就好