线段树的单点更新+区间求和
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 ;
}

线段树基础模板&&扫描线的更多相关文章

  1. Poj 3246 Balanced Lineup(线段树基础)

    依旧是线段树基础题 询问区间的最大值和最小值之差,只有询问,没有插入删除.继续理解基础线段树 #include <iostream> #include <algorithm> ...

  2. P1198 [JSOI2008]最大数(线段树基础)

    P1198 [JSOI2008]最大数 题目描述 现在请求你维护一个数列,要求提供以下两种操作: 1. 查询操作. 语法:Q L 功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值. 限制: ...

  3. 矩形面积并-扫描线 线段树 离散化 模板-poj1151 hdu1542

    今天刚看到这个模板我是懵逼的,这个线段树既没有建树,也没有查询,只有一个update,而且区间成段更新也没有lazy标记....研究了一下午,我突然我发现我以前根本不懂扫描线,之所以没有lazy标记, ...

  4. HDU 1542.Atlantis-线段树求矩形面积并(离散化、扫描线/线段树)-贴模板

    好久没写过博客了,这学期不是很有热情去写博客,写过的题也懒得写题解.现在来水一水博客,写一下若干年前的题目的题解. Atlantis Time Limit: 2000/1000 MS (Java/Ot ...

  5. 【原创】线段树query模板对比! 新手线段树的一个容易出错的问题!!因为我就糊涂了一整天.......

    我们解决问题的最好方法就是拿实例来举例子 我们来看tyvj1038或计蒜客 “管家的忠诚” 老管家是一个聪明能干的人.他为财主工作了整整10年,财主为了让自已账目更加清楚.要求管家每天记k次账,由于管 ...

  6. BZOJ-1036 树的统计Count 链剖线段树(模板)=(树链剖分+线段树)

    潇爷昨天刚刚讲完...感觉得还可以...对着模板打了个模板...还是不喜欢用指针.... 1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec Memory Lim ...

  7. HDU 1754 I Hate It(线段树基础应用)

    基础线段树 #include<iostream> #include<cstdio> #include<cstring> using namespace std; # ...

  8. 【Codeforces】【网络流】【线段树】【扫描线】Oleg and chess (CodeForces - 793G)

    题意: 给定一个n*n的矩阵,一个格子上可以放一个车.其中有q个子矩阵,且q个子矩阵互不相交或者是重叠(但边界可以衔接).这q个子矩阵所覆盖的地方都是不能够放车的.车可以越过子矩阵覆盖的地方进行攻击( ...

  9. Zeratul的完美区间(线段树||RMQ模板题)

    原题大意:原题链接 给定元素无重复数组,查询给定区间内元素是否连续 解体思路:由于无重复元素,所以如果区间内元素连续,则该区间内的最大值和最小值之差应该等于区间长度(r-l) 解法一:线段树(模板题) ...

随机推荐

  1. 多日期选择jQuery插件 MultiDatesPicker for jQuery UI

    Multiple-Dates-Picker-for-jQuery-UI是一个多日期选择的jquery控件.   GIT源码: https://github.com/dubrox/Multiple-Da ...

  2. 关闭和启动adb服务命令

    在运行中输入 关闭——adb kill-server 重启——adb start-server

  3. Thrift 的原理和使用

    thrift 的原理和使用 Thrift 架构 Thrift是一个跨语言的服务部署框架,最初由Facebook于2007年开发,2008年进入Apache开源项目.Thrift通过IDL(Interf ...

  4. [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 ...

  5. swoole 教程

    环境安装:http://blog.csdn.net/ldy3243942/article/details/40263735 Task使用以及swoole_client:http://blog.csdn ...

  6. Yii2 menu navbar nav小部件的使用示例

    menu Menu::widget( [ [ 'label' => $menu['name'], 'url' => [$menu['route']], 'items' => [ [ ...

  7. SQL Server2008 MERGE指令用法

    参考资料: 百度百科-MERGE

  8. Java Programming Test Question 2

    public class JPTQuestion2 { public static void main(String[] args) { String s3 = "JournalDev&qu ...

  9. linux 文件操作和权限

    1.touch 创建文件 2.查看文件cat 浏览一个较短文件,行号加上cat -n 3.反向显示内容tac 并不支持-n选项 4.分页显示文件内容more  空格或f 翻页  回车换行  q或者Q退 ...

  10. php:Header

    转自鸟哥的博客: http://www.laruence.com/2007/12/16/308.html PHP header()the function declaration: void head ...