线段树基础模板&&扫描线
线段树的单点更新+区间求和
hdu1166敌兵布阵
Input
第一行一个整数T,表示有T组数据。
每组数据第一行一个正整数N(N<=),表示敌人有N个工兵营地
,接下来有N个正整数,第i个正整数ai代表第i个工兵营地里开始时有ai个人(<=ai<=)。
接下来每行有一条命令,命令有4种形式:
() Add i j,i和j为正整数,表示第i个营地增加j个人(j不超过30)
()Sub i j ,i和j为正整数,表示第i个营地减少j个人(j不超过30);
()Query i j ,i和j为正整数,i<=j,表示询问第i到第j个营地的总人数;
()End 表示结束,这条命令在每组数据最后出现;
每组数据最多有40000条命令
Output
对第i组数据,首先输出“Case i:”和回车,
对于每个Query询问,输出一个整数并回车,表示询问的段中的总人数,这个数保持在int以内。
Sample Input Query
Add
Query
Sub
Add
Query
End
Sample Output
Case : 结构体实现的代码
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=;
int num[maxn];
char str[];
int n;
typedef struct node{
int left,right,data;
node* lchild;
node* rchild;
node(){
left=right=data=;
}
}tree;
int sum;
tree* create(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)>>;
r->lchild=create(a,mid);
r->rchild=create(mid+,b);
r->data=r->lchild->data+r->rchild->data;
}
return r;
} void updata(tree *r,int a,int b){
if(r->left==a&&r->right==a){
r->data+=b;
return;
}
int mid=(r->left+r->right)>>;
if(a<=mid)
updata(r->lchild,a,b);
else{
updata(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)>>;
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+,b);
}
}
int main(){
int t;
int cas=;
scanf("%d",&t);
while(t--){ scanf("%d",&n);
for(int i=;i<=n;i++){
scanf("%d",&num[i]);
}
tree *T;
T=create(,n);
int a,b;
printf("Case %d:\n",cas++);
while(scanf("%s",str)!=EOF){
if(str[]=='E')
break;
scanf("%d%d",&a,&b);
if(str[]=='A')
updata(T,a,b);
else if(str[]=='S')
updata(T,a,-b);
else{
sum=;
find(T,a,b);
printf("%d\n",sum);
}
} }
return ;
} hdu1754单点更新+区间求最大值
在每个测试的第一行,有两个正整数 N 和 M ( <N<=,<M< ),分别代表学生的数目和操作的数目。
学生ID编号分别从1编到N。
第二行包含N个整数,代表这N个学生的初始成绩,其中第i个数代表ID为i的学生的成绩。
接下来有M行。每一行有一个字符 C (只取'Q'或'U') ,和两个正整数A,B。
当C为'Q'的时候,表示这是一条询问操作,它询问ID从A到B(包括A,B)的学生当中,成绩最高的是多少。
当C为'U'的时候,表示这是一条更新操作,要求把ID为A的学生的成绩更改为B。
Output
对于每一次询问操作,在一行里面输出最高成绩。
Sample Input Q
U
Q
Q
U
Q
Sample Output 代码
数组实现
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int Maxn=;
struct node{
int Max,left,right;
}tree[Maxn*];
int val[Maxn];
int create(int root,int left,int right){
tree[root].left=left;
tree[root].right=right;
if(left==right)
return tree[root].Max=val[left];
int a,b,mid=(left+right)>>;
a=create(root<<,left,mid);
b=create(root<<|,mid+,right);
return tree[root].Max=max(a,b);
}
int updata(int root,int pos,int val){
if(pos<tree[root].left||tree[root].right<pos)
return tree[root].Max;
if(tree[root].left==pos&&tree[root].right==pos)
return tree[root].Max=val;
int a,b;
a=updata(root<<,pos,val);
b=updata(root<<|,pos,val);
return tree[root].Max=max(a,b);
}
int cal(int root,int left,int right){
if(tree[root].left>right||tree[root].right<left)
return ;
if(tree[root].left>=left&&tree[root].right<=right)
return tree[root].Max;
int a,b;
a=cal(root<<,left,right);
b=cal(root<<|,left,right);
return max(a,b);
} int main(){
int n,q;
while(scanf("%d%d",&n,&q)!=EOF){
for(int i=;i<=n;i++)
scanf("%d",&val[i]);
int tmp_1=create(,,n);
char c;
getchar();
for(int i=;i<=q;i++){
int a,b;
scanf("%c %d %d",&c,&a,&b);
getchar();
if(c=='Q'){
int ans=cal(,a,b);
printf("%d\n",ans);
}
else{
int tmp_2= updata(,a,b);
}
}
}
return ;
} hdu1698区间更新+区间求和
在一款游戏dota中,有一个人物叫帕吉,他有一个钩子,钩子的每一节可能是不同的材质,有的是铜的,有的是银的
,有的是金的,每一节铜钩子的价值是1,银钩子的价值是2,金钩子的价值是3。帕吉现在想对钩子做一些操作,然后
求出钩子的总价值。
数据的第一行是测试数据组数。
对于每组测试数据,第一行是一个数字N,代表帕吉钩子的节数,
也就是长度;第二行是一个数字Q,代表帕吉要对钩子进行几次操作,接下来Q行,每行有三个数字X、Y和Z,代表把钩
子的第X节到第Y节修改成第Z种材质(第一种:铜;第二种:银;第三种:金)。
对于每组测试数据,输出帕吉钩子的价值。 代码
#include<iostream>
#include<stdio.h>
#include<cstring>
using namespace std; #define MAXSIZE 1000000 int val[MAXSIZE];
int add[<<];
int sum[<<]; struct node
{
int total;
int left;
int right;
int mark; //延时标记
} tree[MAXSIZE*];
//下面两种create都可以,选择一种就可
//int create(int root,int left,int right)
//{
// add[root]=0;
// sum[root]=1;
// tree[root].left=left;
// tree[root].right=right;
// if(left==right)
// return tree[root].total=val[left];
// int middle=(left+right)>>1;
// return tree[root].total=create(root<<1,left,middle)+create(root<<1|1,middle+1,right);
//}
void create(int root,int left,int right)
{
add[root]=;
sum[root]=;//初始值为1,如果为0,有些在更新中没有更新到的节点值就为0了;
tree[root].left=left;
tree[root].right=right;
if(left==right)
return ;
int middle=(left+right)>>;
create(root<<,left,middle);
create(root<<|,middle+,right);
}
// 参数:询问区间左端点,询问区间右端点,每个位置需要增加的值,当前节点序号
void update(int L, int R, int x, int root)
{
if (L<=tree[root].left && tree[root].right<= R)
{
// 当前区间被包含,处理相应附加信息
add[root] = x; // 更新延迟标记
sum[root] = x * (tree[root].right-tree[root].left+);
return; // 暂时不用再向下递归
}
int mid = (tree[root].left+tree[root].right)>>;
if (add[root]) // 延迟标记不为0,说明有未完成的更新,更新之
{
add[root<<] = add[root];
add[root<<|] = add[root];
sum[root<<] = add[root] * (mid-tree[root].left+);
sum[root<<|] = add[root] * (tree[root].right-mid);
add[root] = ; // 不要忘了去除延迟标记
}
if (L <= mid) // 左子区间中包含有更新区间的部分,需要更新
update(L, R, x, root<<);
if (R > mid) // 右子区间中包含有更新区间的部分,需要更新
update(L, R, x, root<<|);
sum[root] = sum[root<<] + sum[root<<|];//从叶子节点向上更新
} int main()
{
int t;
scanf("%d",&t);
for(int i=,n,q; i<=t; i++)
{
memset(sum,,sizeof(sum));
memset(add,,sizeof(add));
scanf("%d%d",&n,&q);
for(int j=; j<=n; j++)
val[j]=;
create(,,n);
for(int j=,x,y,z; j<q; j++)
{
scanf("%d%d%d",&x,&y,&z);
update(x,y,z,);
}
printf("Case %d: The total value of the hook is %d.\n",i,sum[]);
}
} 线段树的区间合并&&求区间的最长公共子序列
假设有一列数{Ai}(≤i≤n),支持如下两种操作: U A B: 将第A个数变成值 B Q A B: 输出区间[A,B]的最长连续递增子序列的长度 区间的最大连续递增子序列(下面统称LCIS)的长度
las[i]??? i 号节点对应区间包含左端点最长的LCIS长度
ras[i]??? i 号节点对应区间包含右端点最长的LCIS长度
mov[i] 表示i节点(线段)对应的LCIS的长度
///i表示的是节点,即线段树节点
例如当前节点i 对应的区间的序列为: 则 las[i] = , ras[i] = , 同时,mov[i] = 对于线段树中某个节点 i ,它的 mov[i] 值应该是以下几种情况的最大值:
()左儿子 ls 节点的 mov[ls] 值
() 右儿子 rs 节点的 mov[rs] 值
() 左右儿子区间合并之后得到的LCIS值。
从刚刚的例子中我们发现: 区间合并得到的LCIS
一定跨越左右儿子两个区间,
因此其必然经过当前节点对应区间中间的两个数!
我们可以用上面的后两个数组来表示出来,即:
///ras[ls] + las[rs]
用语言表达就是:
///从左儿子对应区间的右端点向左延伸的最大LCIS长度 加上
///从右儿子对应区间的左端点向右延伸的最大LCIS长度 首先是建树:
///建树模版
int build(int root,int l,int r)
{
if(l==r)
{
scanf("%d",&a[l]);
ras[root]=las[root]=;
mov[root]=;
return;
}
int mid=(l+r)>>;
build(root<<,l,mid);
build(root<<|,mid+,r);
pushup(l,mid,r,root);
}
void pushup(int l,int mid,int r,int root)
{
las[root]=las[root<<];
ras[root]=ras[root<<|];
mov[root]=;
if(a[mid]<a[mid+])
{
mov[root]=ras[root<<]+las[root<<|]
if(las[root<<]==mid-l+)
las[root]+=las[root<<|];
if(ras[root<<|]==r-mid)
ras[root]+=ras[root<<];
}
mov[root]=max(mov[root],max(mov[root<<],mov[root<<|]));
} ///查询操作
int query(int L, int R,///L,R为要查询的区间, int l, int r, int rt){
if (L<=l && r<=R)
return mov[rt]; int mid=(l+r)>>;
int res=;
if (R <= mid)
return query(L, R, l, mid, rt<<);
else if (L > mid)
return query(L, R, mid+, r, rt<<|);
else {
int t1 = , t2 = , t3 = ;
t1 = query(L, R, l, mid, rt<<);
t2 = query(L, R, mid+, r, rt<<|);
if (va[mid] < va[mid+]) // 注意约束条件
t3 = min(ras[rt<<],mid-L+) + min(las[rt<<|],R-mid);
return max(t3,max(t1,t2));
}
return res;
}
还有一个操作:
() U A B: 将第A个树变成值 B 这个是之前单点更新操作的实现类似
,唯一需要注意的地方就是维护节点信息的时候用到了和建树相同的pushup() 操作 线段树的扫描线问题
输入就是若干个矩形的坐标,求这些矩形的面积并
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<queue>
#include<algorithm>
#include<map>
#include<iomanip>
#define INF 99999999
using namespace std;
const int MAX=+;
int mark[MAX<<];//记录某个区间的下底边比上底边多的个数
double sum[MAX<<];//记录某个区间下底边比上底边多的个数总长度
double Hash[MAX];//对横坐标x离散化 struct seg //线段
{
double l,r,h;
int d;
seg() {}
seg(double x1,double x2,double H,int D):l(x1),r(x2),h(H),d(D) {}//构造函数
bool operator<(const seg &a)const//自定义结构体排序
{
return h<a.h;
}
} s[MAX]; void Upfather(int n,int left,int right){
if(mark[n])
sum[n]=Hash[right+]-Hash[left];//表示该区间整个线段长度可作为底边
else if(left == right)
sum[n]=;//叶子结点区间长度为0,则底边长度为0
else
sum[n]=sum[n<<]+sum[n<<|];
} void Update(int L,int R,int d,int n,int left,int right){
if(L<=left && right<=R) //该区间是当前扫描线段的一部分
{
mark[n]+=d;//更新上下底边之差
Upfather(n,left,right);//更新可用底边长
return;
}
int mid=left+right>>;
if(L<=mid)
Update(L,R,d,n<<,left,mid);
if(R>mid)
Update(L,R,d,n<<|,mid+,right);
Upfather(n,left,right);
} int search(double key,double *x,int n){//Search函数是为了找到x坐标为key时,hash数组的下标
int left=,right=n-;
while(left<=right){
int mid=(left+right)>>;
if(x[mid] == key)
return mid;
else if(x[mid]>key)
right=mid-;
else
left=mid+;
}
return -;
} int main(){
double x1=,y1=,x2=,y2=;
while(x1 != -){
int size=;
while(cin>>x1>>y1>>x2>>y2,x1>=){
if(x1>x2)swap(x1,x2);
if(y1>y2)swap(y1,y2);
Hash[size]=x1;
s[size++]=seg(x1,x2,y1,);
Hash[size]=x2;
s[size++]=seg(x1,x2,y2,-);
}
sort(Hash,Hash+size);//把x坐标从小到大排序
sort(s,s+size);//把线段按高度h从小到大排序
int k=;
for(int i=; i<size; ++i) //去重复端点
{
if(Hash[i] != Hash[i-])
Hash[k++]=Hash[i];
}
double ans=;
for(int i=; i<size; ++i){
int L=search(s[i].l,Hash,k);
int R=search(s[i].r,Hash,k)-;
Update(L,R,s[i].d,,,k-);//扫描线段更新可用底边长
ans+=sum[]*(s[i+].h-s[i].h);//新增加面积
}
cout<<ans<<endl;
}
return ;
}
线段树基础模板&&扫描线的更多相关文章
- Poj 3246 Balanced Lineup(线段树基础)
依旧是线段树基础题 询问区间的最大值和最小值之差,只有询问,没有插入删除.继续理解基础线段树 #include <iostream> #include <algorithm> ...
- P1198 [JSOI2008]最大数(线段树基础)
P1198 [JSOI2008]最大数 题目描述 现在请求你维护一个数列,要求提供以下两种操作: 1. 查询操作. 语法:Q L 功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值. 限制: ...
- 矩形面积并-扫描线 线段树 离散化 模板-poj1151 hdu1542
今天刚看到这个模板我是懵逼的,这个线段树既没有建树,也没有查询,只有一个update,而且区间成段更新也没有lazy标记....研究了一下午,我突然我发现我以前根本不懂扫描线,之所以没有lazy标记, ...
- HDU 1542.Atlantis-线段树求矩形面积并(离散化、扫描线/线段树)-贴模板
好久没写过博客了,这学期不是很有热情去写博客,写过的题也懒得写题解.现在来水一水博客,写一下若干年前的题目的题解. Atlantis Time Limit: 2000/1000 MS (Java/Ot ...
- 【原创】线段树query模板对比! 新手线段树的一个容易出错的问题!!因为我就糊涂了一整天.......
我们解决问题的最好方法就是拿实例来举例子 我们来看tyvj1038或计蒜客 “管家的忠诚” 老管家是一个聪明能干的人.他为财主工作了整整10年,财主为了让自已账目更加清楚.要求管家每天记k次账,由于管 ...
- BZOJ-1036 树的统计Count 链剖线段树(模板)=(树链剖分+线段树)
潇爷昨天刚刚讲完...感觉得还可以...对着模板打了个模板...还是不喜欢用指针.... 1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec Memory Lim ...
- HDU 1754 I Hate It(线段树基础应用)
基础线段树 #include<iostream> #include<cstdio> #include<cstring> using namespace std; # ...
- 【Codeforces】【网络流】【线段树】【扫描线】Oleg and chess (CodeForces - 793G)
题意: 给定一个n*n的矩阵,一个格子上可以放一个车.其中有q个子矩阵,且q个子矩阵互不相交或者是重叠(但边界可以衔接).这q个子矩阵所覆盖的地方都是不能够放车的.车可以越过子矩阵覆盖的地方进行攻击( ...
- Zeratul的完美区间(线段树||RMQ模板题)
原题大意:原题链接 给定元素无重复数组,查询给定区间内元素是否连续 解体思路:由于无重复元素,所以如果区间内元素连续,则该区间内的最大值和最小值之差应该等于区间长度(r-l) 解法一:线段树(模板题) ...
随机推荐
- 多日期选择jQuery插件 MultiDatesPicker for jQuery UI
Multiple-Dates-Picker-for-jQuery-UI是一个多日期选择的jquery控件. GIT源码: https://github.com/dubrox/Multiple-Da ...
- 关闭和启动adb服务命令
在运行中输入 关闭——adb kill-server 重启——adb start-server
- Thrift 的原理和使用
thrift 的原理和使用 Thrift 架构 Thrift是一个跨语言的服务部署框架,最初由Facebook于2007年开发,2008年进入Apache开源项目.Thrift通过IDL(Interf ...
- [webgrid] – header - (How to Add custom html to Header in WebGrid)
How to Add custom html to Header in WebGrid MyEvernote Link Posted on March 30, 2013by mtryambake Ho ...
- swoole 教程
环境安装:http://blog.csdn.net/ldy3243942/article/details/40263735 Task使用以及swoole_client:http://blog.csdn ...
- Yii2 menu navbar nav小部件的使用示例
menu Menu::widget( [ [ 'label' => $menu['name'], 'url' => [$menu['route']], 'items' => [ [ ...
- SQL Server2008 MERGE指令用法
参考资料: 百度百科-MERGE
- Java Programming Test Question 2
public class JPTQuestion2 { public static void main(String[] args) { String s3 = "JournalDev&qu ...
- linux 文件操作和权限
1.touch 创建文件 2.查看文件cat 浏览一个较短文件,行号加上cat -n 3.反向显示内容tac 并不支持-n选项 4.分页显示文件内容more 空格或f 翻页 回车换行 q或者Q退 ...
- php:Header
转自鸟哥的博客: http://www.laruence.com/2007/12/16/308.html PHP header()the function declaration: void head ...