线段树tq(巨佬太强了)l
题目链接
1、hdu1166敌兵布阵
最基础的线段树应用,这里没有用上区间修改,但是这个题可以当个板子
/*递归方法*/
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
using namespace std;
const int maxn = 50000 + 5;//元素总个数
int p[4 * maxn],add[4 * maxn];//p各节点信息,add惰性标记
int A[maxn],n;//存储原数组下标对应信息
/*rt 当前节点序号,l,r 当前节点区间,c 修改点或者区间的值*/
//自下向上对节点的更新
void PushUp(int rt)
{
p[rt] = p[2 * rt] + p[2 * rt + 1];
}
void PushDown(int rt,int ln,int rn)
{
//ln,rn为左子树,右子树的数字数量
if(add[rt]){//是否修改过
//下推标记
add[2 * rt] += add[rt];
add[2 * rt + 1] += add[rt];//表示两个字节点也被修改
p[2 * rt] += add[rt] * ln;
p[2 * rt + 1] += add[rt] * rn;
//消除当前节点标记,表示已经下推给下一层节点
add[rt] = 0;
}
}
//创建树
void Build(int l,int r,int rt)
{
//如果抵达叶节点
if(l == r){
p[rt] = A[l];
return ;
}
int m = (l + r) / 2;
//左右递归
Build(l,m,2 * rt);
Build(m + 1,r,2 * rt + 1);
//更新当层节点信息
PushUp(rt);
}
//点修改
void Point_Update(int L,int c,int l,int r,int rt)//L 要修改的点
{
//找到要修改的叶节点
if(l == r){
p[rt] += c;
return ;
}
int m = (l + r) / 2;
//判断节点所在哪个区间,同二分查找
if(L <= m){
Point_Update(L,c,l,m,2 * rt);
}
else{
Point_Update(L,c,m + 1,r,2 * rt + 1);
}
//子节点更新了,本节点也要进行更新
PushUp(rt);
}
//区间修改
void Line_Update(int L,int R,int c,int l,int r,int rt)//L R表示要操作区间
{
//如果当前区间完全在要进行修改的区间内
if(L <= l&&r <= R){
//更新数字和,向上保持正确
p[rt] += c * (r - l + 1);
//增加标记表示本区间已经进行修改,并根据当前add值修改下一层子节点区间
add[rt] += c;
return ;
}
int m = (l + r) / 2;
PushDown(rt,m - l + 1,r - m);//下推add标记,若下一层标记需要根据标记修改则修改
//这里判断左右子树跟[L,R]有无交集,有交集才递归
if(L <= m){
Line_Update(L,R,c,l,m,2 * rt);
}
if(R > m){
Line_Update(L,R,c,m + 1,r,2 * rt + 1);
}
//更新本节点信息
PushUp(rt);
}
//区间查询函数
int Query(int L,int R,int l,int r,int rt)
{
//当前区间在完全在要查询区间内,那么直接返回该区间值
if(L <= l&&r <= R){
return p[rt];
}
int m = (l + r) / 2;
//下推标记,不然返回的可能是修改之前的值
PushDown(rt,m - l + 1,r - m);
//累计答案
int ans = 0;
if(L <= m){
ans += Query(L,R,l,m,2 * rt);
}
if(R > m){
ans += Query(L,R,m + 1,r,2 * rt + 1);
}
return ans;
}
int main()
{
int t,count = 0;
cin>>t;
while(t--){
count++;
printf("Case %d:\n",count);
memset(A,0,sizeof(A));
memset(p,0,sizeof(p));
memset(add,0,sizeof(add));
scanf("%d",&n);
for(int i = 1; i <=n; i++){
scanf("%d",&A[i]);
}
int Q_ans;
char oper[15];
Build(1,n,1);
while(scanf("%s",oper)&&strcmp(oper,"End")){
if(!strcmp(oper,"Query")){
int l,r;
scanf("%d%d",&l,&r);
Q_ans = Query(l,r,1,n,1);
printf("%d\n",Q_ans);
}
if(!strcmp(oper,"Add")){
int L,c;
scanf("%d%d",&L,&c);
Point_Update(L,c,1,n,1);
}
if(!strcmp(oper,"Sub")){
int L,c;
scanf("%d%d",&L,&c);
c = -c;
Point_Update(L,c,1,n,1);
}
}
}
return 0;
}
2、hdu1754 I Hate it
也是模板题,就是把加法换成取最大就彳亍
/*递归方法*/
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
using namespace std;
const int maxn = 200005;//元素总个数
int p[4 * maxn],add[4 * maxn];//p各节点信息,add惰性标记
int A[maxn],n;//存储原数组下标对应信息
/*rt 当前节点序号,l,r 当前节点区间,c 修改点或者区间的值*/
//自下向上对节点的更新
void PushUp(int rt)
{
p[rt] = max(p[2 * rt],p[2 * rt + 1]);
}
void PushDown(int rt,int ln,int rn)
{
//ln,rn为左子树,右子树的数字数量
if(add[rt]){//是否修改过
//下推标记
add[2 * rt] += add[rt];
add[2 * rt + 1] += add[rt];//表示两个字节点也被修改
p[2 * rt] += add[rt] * ln;
p[2 * rt + 1] += add[rt] * rn;
//消除当前节点标记,表示已经下推给下一层节点
add[rt] = 0;
}
}
//创建树
void Build(int l,int r,int rt)
{
//如果抵达叶节点
if(l == r){
p[rt] = A[l];
return ;
}
int m = (l + r) / 2;
//左右递归
Build(l,m,2 * rt);
Build(m + 1,r,2 * rt + 1);
//更新当层节点信息
PushUp(rt);
}
//点修改
void Point_Update(int L,int c,int l,int r,int rt)//L 要修改的点
{
//找到要修改的叶节点
if(l == r){
p[rt] = c;
return ;
}
int m = (l + r) / 2;
//判断节点所在哪个区间,同二分查找
if(L <= m){
Point_Update(L,c,l,m,2 * rt);
}
else{
Point_Update(L,c,m + 1,r,2 * rt + 1);
}
//子节点更新了,本节点也要进行更新
PushUp(rt);
}
//区间修改,这里不需要,因为改成绩这里是对应点进行修改
void Line_Update(int L,int R,int c,int l,int r,int rt)//L R表示要操作区间
{
//如果当前区间完全在要进行修改的区间内
if(L <= l&&r <= R){
//更新数字和,向上保持正确
p[rt] += c * (r - l + 1);
//增加标记表示本区间已经进行修改,并根据当前add值修改下一层子节点区间
add[rt] += c;
return ;
}
int m = (l + r) / 2;
PushDown(rt,m - l + 1,r - m);//下推add标记,若下一层标记需要根据标记修改则修改
//这里判断左右子树跟[L,R]有无交集,有交集才递归
if(L <= m){
Line_Update(L,R,c,l,m,2 * rt);
}
if(R > m){
Line_Update(L,R,c,m + 1,r,2 * rt + 1);
}
//更新本节点信息
PushUp(rt);
}
//区间查询函数
int Query(int L,int R,int l,int r,int rt)
{
//当前区间在完全在要查询区间内,那么直接返回该区间值
if(L <= l&&r <= R){
return p[rt];
}
int m = (l + r) / 2;
int ans = 0;
if(L <= m){
ans = max(ans,Query(L,R,l,m,2 * rt));
}
if(R > m){
ans = max(ans,Query(L,R,m + 1,r,2 * rt + 1));
}
return ans;
}
int main()
{
int m,n;
while(scanf("%d%d",&n,&m) == 2){
for(int i = 1; i <= n; i++){
scanf("%d",&A[i]);
}
Build(1,n,1);
int a,b;
char str[2];
while(m--){
scanf("%s%d%d",str,&a,&b);
if(str[0] == 'Q'){
int ans = Query(a,b,1,n,1);
printf("%d\n",ans);
}
else{
Point_Update(a,b,1,n,1);
}
}
}
return 0;
}
3、poj3264 Balanced Lineup
这个也很简单,但是需要剪枝
这个是tle代码,就是没有剪枝的
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
using namespace std;
const int maxn = 50000 + 5;//元素总个数
const int inf = 1e9;
struct point_cow
{
int minx;
int maxx;
};
point_cow p[4 * maxn];//p各节点信息
int A[maxn];//存储原数组下标对应信息
/*rt 当前节点序号,l,r 当前节点区间,c 修改点或者区间的值*/
//自下向上对节点的更新
void PushUp(int rt)
{
p[rt].maxx = max(p[2 * rt].maxx,p[2 * rt + 1].maxx);
p[rt].minx = min(p[2 * rt].minx,p[2 * rt + 1].minx);
}
//创建树
void Build(int l,int r,int rt)
{
//如果抵达叶节点
if(l == r){
p[rt].maxx = p[rt].minx = A[l];
return ;
}
int m = (l + r) / 2;
//左右递归
Build(l,m,2 * rt);
Build(m + 1,r,2 * rt + 1);
//更新当层节点信息
PushUp(rt);
}
//区间查询函数
point_cow Query(int L,int R,int l,int r,int rt)
{
//当前区间在完全在要查询区间内,那么直接返回该区间值
if(L <= l&&r <= R){
return p[rt];
}
int m = (l + r) / 2;
point_cow ans;
ans.maxx = 0;
ans.minx = inf;
if(L <= m){
ans.maxx = max(ans.maxx,Query(L,R,l,m,2 * rt).maxx);
ans.minx = min(ans.minx,Query(L,R,l,m,2 * rt).minx);
}
if(R > m){
ans.maxx = max(ans.maxx,Query(L,R,m + 1,r,2 * rt + 1).maxx);
ans.minx = min(ans.minx,Query(L,R,m + 1,r,2 * rt + 1).minx);
}
return ans;
}
int main()
{
int n,q;
scanf("%d%d",&n,&q);
for(int i = 1; i <= n; i++){
scanf("%d",&A[i]);
}
Build(1,n,1);
//cout<<p[2].maxx<<' '<<p[2].minx<<endl;
//cout<<p[6].maxx<<' '<<p[6].minx<<endl;
while(q--){
int l,r;
scanf("%d%d",&l,&r);
point_cow ans = Query(l,r,1,n,1);
int res = ans.maxx - ans.minx;
printf("%d\n",res);
}
return 0;
}
这个是剪枝之后的
/*递归方法*/
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
using namespace std;
const int maxn = 50000 + 5;//元素总个数
const int inf = 1e9;
struct point_cow
{
int minx;
int maxx;
};
point_cow p[4 * maxn];//p各节点信息
int A[maxn];//存储原数组下标对应信息
int max1,min1;
/*rt 当前节点序号,l,r 当前节点区间,c 修改点或者区间的值*/
//自下向上对节点的更新
void PushUp(int rt)
{
if(p[2 * rt].maxx > p[2 * rt + 1].maxx){
p[rt].maxx = p[2 * rt].maxx;
}
else{
p[rt].maxx = p[2 * rt + 1].maxx;
}
if(p[2 * rt].minx < p[2 * rt + 1].minx){
p[rt].minx = p[2 * rt].minx;
}
else{
p[rt].minx = p[2 * rt + 1].minx;
}
}
//创建树
void Build(int l,int r,int rt)
{
//如果抵达叶节点
if(l == r){
p[rt].maxx = p[rt].minx = A[l];
return ;
}
int m = (l + r) / 2;
//左右递归
Build(l,m,2 * rt);
Build(m + 1,r,2 * rt + 1);
//更新当层节点信息
PushUp(rt);
}
//区间查询函数
void Query(int L,int R,int l,int r,int rt)
{
//剪枝
if(p[rt].minx >= min1&&p[rt].maxx <= max1){
return ;
}
//当前区间在完全在要查询区间内,那么直接返回该区间值
if(L <= l&&r <= R){
min1 = min(p[rt].minx,min1);
max1 = max(p[rt].maxx,max1);
return ;
}
int m = (l + r) / 2;
if(L <= m){
Query(L,R,l,m,2 * rt);
}
if(R > m){
Query(L,R,m + 1,r,2 * rt + 1);
}
}
int main()
{
int n,q;
scanf("%d%d",&n,&q);
for(int i = 1; i <= n; i++){
scanf("%d",&A[i]);
}
Build(1,n,1);
//cout<<p[2].maxx<<' '<<p[2].minx<<endl;
//cout<<p[6].maxx<<' '<<p[6].minx<<endl;
while(q--){
int l,r;
max1 = 0;
min1 = inf;
scanf("%d%d",&l,&r);
Query(l,r,1,n,1);
int res = max1 - min1;
printf("%d\n",res);
}
return 0;
}
4、对某区间内所有数进行操作
hdu4027 Can you answer these queries?
也是模板题,但是在于如何对给定区间内的数进行单独操作
类似于单点操作,但是函数的参数由点的序号变成了区间左右序号
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;//元素总个数
ll p[4 * maxn],add[4 * maxn];//p各节点信息,add惰性标记
ll A[maxn];//存储原数组下标对应信息
/*rt 当前节点序号,l,r 当前节点区间,c 修改点或者区间的值*/
//自下向上对节点的更新
void PushUp(int rt)
{
p[rt] = p[2 * rt] + p[2 * rt + 1];
}
//创建树
void Build(int l,int r,int rt)
{
//如果抵达叶节点
if(l == r){
p[rt] = A[l];
return ;
}
int m = (l + r) / 2;
//左右递归
Build(l,m,2 * rt);
Build(m + 1,r,2 * rt + 1);
//更新当层节点信息
PushUp(rt);
}
//将区间内的每个点进行修改
void Point_Update(int L,int R,int l,int r,int rt)
{
if(l == r){
p[rt] = sqrt(p[rt]);
return ;
}
//剪枝
if(L <= l&&r <= R&&p[rt] == r - l + 1){
return ;
}
int m = (l + r) / 2;
if(L <= m){
Point_Update(L,R,l,m,2 * rt);
}
if(R > m){
Point_Update(L,R,m + 1,r,2 * rt + 1);
}
PushUp(rt);
}
//区间查询函数
ll Query(int L,int R,int l,int r,int rt)
{
//当前区间在完全在要查询区间内,那么直接返回该区间值
if(L <= l&&r <= R){
return p[rt];
}
int m = (l + r) / 2;
//累计答案
ll ans = 0;
if(L <= m){
ans += Query(L,R,l,m,2 * rt);
}
if(R > m){
ans += Query(L,R,m + 1,r,2 * rt + 1);
}
return ans;
}
int main()
{
int n,m,count = 0;
while(scanf("%d",&n) == 1){
for(int i = 1; i <= n; i++){
scanf("%lld",&A[i]);
}
Build(1,n,1);
scanf("%d",&m);
count++;
printf("Case #%d:\n",count);
while(m--){
int mark,l,r;
scanf("%d%d%d",&mark,&l,&r);
//!!!
if(l > r){
swap(l,r);
}
if(mark){
ll ans = Query(l,r,1,n,1);
printf("%lld\n",ans);
}
else{
Point_Update(l,r,1,n,1);
}
}
printf("\n");
}
return 0;
}
5、关于连续区间 hdu1540http://acm.hdu.edu.cn/showproblem.php?pid=1540
题意:
区间1-N M次询问D 表示摧毁该村子通向其他的道路,R表示恢复上次摧毁的道路,Q表示询问该村庄和几个连在一起
思路:
利用线段树求区间中被毁村庄的最大最小值 以样例来说 1 2 3 4 5 6 7将3 5 6摧毁后只需求出区间1->4中被摧毁序号最大村庄 2 号和4 -> 7中被摧毁序号最小村庄5号,这样4号连续连续的村庄数量为min - max -1,但是要特判,如果max = min时,输出0,也即是该点村庄正好是被摧毁的。
代码
#include <iostream>
#include <cstring>
#include <cstdio>
#include <stack>
using namespace std;
const int maxn = 50015;
const int inf = 1e9;
struct point
{
int maxx;
int minx;
}node[4 * maxn];
int n;
void PushUp(int rt)
{
node[rt].maxx = max(node[2 * rt].maxx,node[2 * rt + 1].maxx);
node[rt].minx = min(node[2 * rt].minx,node[2 * rt + 1].minx);
}
void Build(int l,int r,int rt)
{
if(l == r){
node[rt].maxx = 0;
node[rt].minx = n + 1;
return ;
}
int m = (l + r) / 2;
Build(l,m,2 * rt);
Build(m + 1,r,2 * rt + 1);
PushUp(rt);
}
void Update(int point,int pmax,int pmin,int l,int r,int rt)
{
if(l == r){
node[rt].maxx = pmax;
node[rt].minx = pmin;
return ;
}
int m = (l + r) / 2;
if(point <= m){
Update(point,pmax,pmin,l,m,2 * rt);
}
if(point > m){
Update(point,pmax,pmin,m + 1,r,2 * rt + 1);
}
PushUp(rt);
}
int max_Query(int L,int R,int l,int r,int rt)
{
if(L <= l&&r <= R){
return node[rt].maxx;
}
int m = (l + r) / 2;
int ans = 0;
if(L <= m){
ans = max(ans,max_Query(L,R,l,m,2 * rt));
}
if(R > m){
ans = max(ans,max_Query(L,R,m + 1,r,2 * rt + 1));
}
return ans;
}
int min_Query(int L,int R,int l,int r,int rt)
{
if(L <= l&&r <= R){
return node[rt].minx;
}
int m = (l + r) / 2;
int ans = inf;
if(L <= m){
ans = min(ans,min_Query(L,R,l,m,2 * rt));
}
if(R > m){
ans = min(ans,min_Query(L,R,m + 1,r,2 * rt + 1));
}
return ans;
}
int main()
{
int t;
while(scanf("%d%d",&n,&t) == 2){
Build(1,n,1);
stack <int> sta;
char oper[2];
while(t--){
int num;
scanf("%s",oper);
if(oper[0] == 'D'){
scanf("%d",&num);
Update(num,num,num,1,n,1);
sta.push(num);
}
if(oper[0] == 'Q'){
int x,y;
scanf("%d",&num);
x = max_Query(1,num,1,n,1);
y = min_Query(num,n,1,n,1);
if(x == y){
printf("0\n");
}
else{
printf("%d\n",y - x - 1);
}
}
if(oper[0] == 'R'){
num = sta.top();
sta.pop();
Update(num,0,n + 1,1,n,1);
}
}
}
return 0;
}
6、多重懒标记
https://blog.csdn.net/CUCUC1/article/details/107350477
7、离散化 + 线段树
https://blog.csdn.net/CUCUC1/article/details/107370653
8、线段树 + 二分
https://blog.csdn.net/CUCUC1/article/details/107378996
9、区间合并
https://blog.csdn.net/CUCUC1/article/details/108538721
&spm=1001.2101.3001.5002&articleId=107284258&d=1&t=3&u=3162dbc8c5164c09a23d2323c65d82aa)
1094

被折叠的 条评论
为什么被折叠?



