A - 一棵简单的线段树

A[1...n]初始全为0.

1. 给两个数p 和 x(1≤p≤n),单点更新 A[p] <- x

2. 给两个数L和R (1≤L<R≤n),  L到R区间里这几个数去掉一个最大值和一个最小值后剩下的数的和是多少。用到了max, min, sum

 #include <iostream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1e6+;
#define LL long long
#define INF 0x7fffffff int n,a[maxn],q; struct node
{
int l,r,mx,mn;
long long sum;
void update(long long x) {sum=x;mx=x;mn=x;}
}tree[maxn*]; void push_up(int x)
{
tree[x].sum=tree[x<<].sum+tree[x<<|].sum;
tree[x].mx=max(tree[x<<].mx,tree[x<<|].mx);
tree[x].mn=min(tree[x<<].mn,tree[x<<|].mn);
} void build(int x,int l,int r)
{
tree[x].l=l,tree[x].r=r;
tree[x].sum=,tree[x].mx=,tree[x].mn=;
if(l==r)
{
tree[x].sum=tree[x].mx=tree[x].mn=a[l];
}
else
{
int mid = (l+r)/;
build(x<<,l,mid);
build(x<<|,mid+,r);
push_up(x);
}
} void update(int x,int pos,long long val)
{
int L =tree[x].l, R = tree[x].r;
if(L==pos&&R==pos)
{
tree[x].update(val);
}
else
{
int mid = (L+R)/;
if(mid>=pos)update(x<<,pos,val);
if(pos>mid)update(x<<|,pos,val);
push_up(x);
}
} long long query(int x,int l,int r)
{
int L =tree[x].l, R = tree[x].r;
if(l<=L&&R<=r)
return tree[x].sum;
else
{
long long ans=;
int mid = (L+R)/;
if(mid>=l)ans+=query(x<<,l,r);
if(r>mid)ans+=query(x<<|,l,r);
push_up(x);
return ans;
}
} int query_max(int x,int l,int r)
{
int L =tree[x].l, R = tree[x].r;
if(l<=L&&R<=r)
return tree[x].mx;
else
{
int ans=-1e9-;
int mid = (L+R)/;
if(mid>=l)ans=max(ans,query_max(x<<,l,r));
if(r>mid)ans=max(ans,query_max(x<<|,l,r));
push_up(x);
return ans;
}
} int query_min(int x,int l,int r)
{
int L =tree[x].l, R = tree[x].r;
if(l<=L&&R<=r)
return tree[x].mn;
else
{
int ans=1e9+;
int mid = (L+R)/;
if(mid>=l)ans=min(ans,query_min(x<<,l,r));
if(r>mid)ans=min(ans,query_min(x<<|,l,r));
push_up(x);
return ans;
}
} int main() {
scanf("%d",&n);
for(int i=;i<=n;i++)a[i]=;
build(,,n);
scanf("%d",&q);
for(int i=;i<=q;i++)
{
int x,y,o;
scanf("%d%d%d",&o,&x,&y);
if(o==)update(,x,y);
else
{
long long sum=query(,x,y);
int mx=query_max(,x,y);
int mn=query_min(,x,y);
printf("%lld\n",sum-mx-mn);
}
}
return ;
}

B - 一棵普通的线段树

A[1...n]初始全为0。

一棵裸的区间修改、区间查询的线段树

1. 区间加v

2. 区间求和

区间修改(带lazy 标记)、区间查询的线段树可以在 O(log n) 的时间内维护区间和
 #include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1e6+;
#define LL long long
#define INF 0x7fffffff int n,q,a[maxn]; struct node{
int l, r;
LL sum, lazy;
void update(int x) {
sum += 1LL*(r-l+)*x;
lazy += x;
}
}tree[maxn*]; void push_up(int x) {
tree[x].sum = tree[x<<].sum + tree[x<<|].sum;
} void push_down(int x) {
int lazy = tree[x].lazy;
if(lazy) {
tree[x<<].update(lazy);
tree[x<<|].update(lazy);
tree[x].lazy = ;
}
} void build(int x, int l, int r) {
tree[x].l = l, tree[x].r = r;
tree[x].sum = tree[x].lazy = ;
if(l == r) {
tree[x].sum = a[l];
}
else {
int mid = (l+r) / ;
build(x<<, l, mid);
build(x<<|, mid+, r);
push_up(x);
}
} void update(int x, int l, int r, int val) {
int L = tree[x].l, R = tree[x].r;
if(l <= L && R <= r) {
tree[x].update(val);
}
else {
push_down(x);
int mid = (L+R) / ;
if(mid >= l) update(x<<, l, r, val);
if(r > mid) update(x<<|, l, r, val);
push_up(x);
}
} LL query(int x, int l, int r) {
int L = tree[x].l, R = tree[x].r;
if(l <= L && R <= r) return tree[x].sum;
else {
push_down(x);
int mid = (L+R) / ;
LL ans = ;
if(mid >= l) ans += query(x<<, l, r);
if(r > mid) ans += query(x<<|, l, r);
push_up(x);
return ans;
}
} int main() {
scanf("%d",&n);
for(int i=;i<=n;i++)a[i]=;
build(,,n);
scanf("%d",&q);
for(int i=;i<=q;i++)
{
int o,l,r,val;
scanf("%d%d%d%d",&o,&l,&r,&val);
if(o==)update(,l,r,val);
else printf("%lld\n",query(,l,r));
}
return ;
}

C - 一棵像样的线段树

假设每个Ci = i , bn最大为n+1。

对于每个Ci,相当于询问b[i-ci] 到 b[i-1] 区间内最小未出现的数。

可以设s[x] 为x最后出现的位置。对于每个Ci,我们可以得到左区间 i - Ci;

s[1]= 0 , 其他位置初始化-1(其他值都未出现过,所以都满足s[x] < i - ci )

凡是s[x] < i - Ci 的x均未在区间内出现过,那么我们只要找出最小的x,满足:s[x] < i - ci 即可。 

可以用线段树维护s[x] ,1 <= x <= n+1 , 区间的最小值。

每次给出左区间 i - ci ,就从最大的区间查询,如果左子树的最小值>=i-ci,就递归访问右子树,否则访问左子树。

最终区间长度为1时,l(或r)即为答案。

总结:可以发现b数组最多就是1到n,那么对于1到n开一棵线段树,每个结点储存当前数出现的最后的位置。因为我们要求的是 i-ci到i-1的范围内没有出现过的第一个数,

那么我们现在储存了每个数出现的最后的位置,那么我们就找一个这个位置不在i-ci到i-1的范围内且最小的数即可。因为我们线段树是默认1到n即从小到大,所以即尽可能地往左子树找,

 #include <bits/stdc++.h>
using namespace std; const int N = 1e6+;
const int maxn = N<<; int n,a[N];
int tree[maxn];
int ans[N]; void update_tree(int a, int b, int pos, int node, int value) {
if(a>b || a>pos || b<pos) return;
if(a==b) {
tree[node]=value;
return;
}
update_tree(a,(a+b)>>,pos,node<<,value);
update_tree(((a+b)>>)+,b,pos,(node<<)|,value);
tree[node]=min(tree[node<<],tree[(node<<)|]);
} int query_tree(int a, int b, int node, int value) {
if(a==b) return a;
if(tree[node<<]<value) return query_tree(a,(a+b)>>,node<<,value);
else return query_tree(((a+b)>>)+,b,(node<<)|,value);
} int main() {
int i;
scanf("%d", &n);
for(i=;i<=n;i++)
{
scanf("%d", &a[i]);
}
for(i=;i<=n;i++)
{
if(i==)update_tree(,,,,);
else update_tree(,,ans[i],,i);
int l=i-a[i]+;
ans[i+]=query_tree(,,,l);
}
for(i=;i<=n+;i++)
if(i<n+)printf("%d ", ans[i]);
else printf("%d\n", ans[i]); return ;
} #include <bits/stdc++.h>
using namespace std; const int N = 1e6+;
const int maxn = N<<; int n, a[N], ans[N]; struct node
{
int l,r;
int min_left;
void update(int value){min_left=value;}
}tree[maxn]; void push_up(int x)
{
tree[x].min_left = min(tree[x<<].min_left,tree[x<<|].min_left);
} void build(int x,int l,int r)
{
tree[x].l=l,tree[x].r=r;
if(l==r)
{
tree[x].min_left = -;
}
else
{
int mid = (l+r)/;
build(x<<,l,mid);
build(x<<|,mid+,r);
push_up(x);
}
}
void update_tree(int x, int l, int r, int pos, int value) {
if(l>r || l>pos || r<pos) return;
if(l==r)
{
tree[x].update(value);
}
else
{
int mid = (l+r)/;
if(pos<=mid)update_tree(x<<,l,mid,pos,value);
if(pos<=r)update_tree(x<<|,mid+,r,pos,value);
push_up(x);
}
} int query_tree(int x, int l, int r, int value) {
if(l==r) return l;
int mid = (l+r)/;
if(tree[x<<].min_left<value) return query_tree(x<<,l,mid,value);
else return query_tree(x<<|,mid+,r,value);
} int main() {
int i;
scanf("%d", &n);
for(i=;i<=n;i++)
{
scanf("%d", &a[i]);
}
build(,,);
for(i=;i<=n;i++)
{
if(i==)update_tree(,,,,);
else update_tree(,,,ans[i],i);
int l=i-a[i]+;
ans[i+]=query_tree(,,,l);
}
for(i=;i<=n+;i++)
if(i<n+)printf("%d ", ans[i]);
else printf("%d\n", ans[i]); return ;
}

D - 一棵复杂的线段树

A[1...n]为1到n的一个全排列

对[L,R]区间,升序排序或降序排序

排完后查找第K个数

思路:线段树+二分答案

结果只要求a[k]的值,并且a数列是1-n的一个全排列,那么可以二分答案。 
排序过程不好想,假设mid为答案,将>mid的设为1,<=mid的设为0。
当对 [L,R] 区间进行排序时, 
1. 先求出区间内1的个数。 
2. 区间全置为0. 
3. 升序排序时,将最后c个数 ( [R-c+1, R] ) 置为1. 
4. 降序排序时,将前c个数( [L, L + c - 1] )置为1. 
所有操作进行结束后,如果 [k, k] == 1 说明 a[k] > mid ( li = mid + 1 ) , 为 0 说明 a[k] <= mid ( ri = mid ) 。

 #include <bits/stdc++.h>
using namespace std;
const int AX = 1e5+;
int lazy[AX<<];
int s[AX<<];
int a[AX];
int op[AX];
int l[AX];
int r[AX];
int n,k;
int tot; void push_down( int rt , int l , int r ){
if( lazy[rt] != - ){
int mid = ( l + r ) >> ;
s[rt<<] = ( mid - l + ) * lazy[rt] ;
s[rt<<|] = ( r - mid ) * lazy[rt];
lazy[rt<<] = lazy[rt];
lazy[rt<<|] = lazy[rt];
lazy[rt] = -;
}
return ;
} void push_up( int rt ){
s[rt] = s[rt<<] + s[rt<<|];
return ;
} void build( int l , int r , int rt , int v ){
lazy[rt] = -;
if( l == r ){
s[rt] = ( a[l] > v ) ;
return ;
}
int mid = ( l + r ) >> ;
build( l , mid , rt << , v );
build( mid + , r , rt << | , v);
push_up(rt);
} void update( int L , int R , int v , int l , int r , int rt ){
if( L <= l && r <= R ){
s[rt] = v * ( r - l + );
lazy[rt] = v;
return;
}
push_down(rt,l,r);
int mid = ( l + r ) >> ;
if( L <= mid ) update( L , R , v , l , mid , rt << );
if( R > mid ) update( L , R , v , mid + , r , rt << | );
push_up(rt);
} int query( int L , int R , int l , int r , int rt ){
if( L <= l && r <= R ){
return s[rt];
}
push_down(rt,l,r);
int ans = ;
int mid = ( l + r ) >> ;
if( L <= mid ) ans += query( L , R , l , mid, rt << );
if( R > mid ) ans += query( L , R , mid + , r , rt << | );
return ans ;
} int main(){
scanf("%d%d",&n,&k);
for( int i = ; i <= n ; i++ ){
scanf("%d",&a[i]);
}
int m ;
scanf("%d",&m);
for( int i = ; i < m ; i++ ){
scanf("%d%d%d",&op[i],&l[i],&r[i]);
}
int li = , ri = n ;
while( li < ri ){ // [li,ri]
int mid = ( li + ri ) >> ;
build( , n , , mid );
for( int i = ; i < m ; i++ ){
int L = l[i];
int R = r[i];
int c = query( L , R , , n , ); // 区间查询 > mid 的个数
update( L , R , , , n , ); // 区间更新 为0
if( op[i] ){
if( L <= L + c - ){
update( L , L + c - , , , n , ); // 区间更新
}
}else{
if( R - c + <= R ){
update( R - c + , R , , , n , ); // 区间更新
}
}
}
if( query( k , k , , n , )) li = mid + ; // 单点查询
else ri = mid;
}
printf("%d\n",li);
return ;
}

2018 UESTC 线段树专题的更多相关文章

  1. zkw线段树专题

    题目来自大神博客的线段树专题 http://www.notonlysuccess.com/index.php/segment-tree-complete/ hdu1166 敌兵布阵题意:O(-1)思路 ...

  2. vj线段树专题

    vj线段树专题题解 单点更新模板 void build(int x,int l,int r){//sum[x]控制l-r区域 if(l==r){Sum[x]=num[l];return ;} int ...

  3. Kuangbin 带你飞-线段树专题 题解

    HDU 1166 敌兵布阵 单调更新区间查询和 #include <map> #include <set> #include <list> #include < ...

  4. 线段树专题—ZOJ1610 Count the Colors

    题意:给一个n,代表n次操作,接下来每次操作表示把[l.r]区间的线段涂成k的颜色当中,l,r,k的范围都是0到8000 分析:事实上就是拿线段树维护一段区间的颜色,整体用到的是线段树的区间更新把,可 ...

  5. [九省联考2018] IIIDX 线段树+贪心

    题目: 给出 k 和 n 个数,构造一个序列使得 d[i]>=d[i/k] ,并且字典序最大. 分析: 听说,当年省选的时候,这道题挡住了大批的高手,看上去十分简单,实际上那道弯段时间内是转不过 ...

  6. 线段树专题 POJ3468 A Simple Problem with Integers

    题意:n个点.m个操作.两种操作类型.C X Y K 表示区间[x,y]上每一个点值加k.Q X Y 求区间[x,y]的和 分析:线段树区间求和,裸模板 注意:结果会超int,要用long long ...

  7. [BZOJ5249][九省联考2018]IIIDX(线段树)

    5249: [2018多省省队联测]IIIDX Time Limit: 40 Sec  Memory Limit: 512 MBSubmit: 32  Solved: 17[Submit][Statu ...

  8. 线段树专题2-(加强版线段树-可持续化线段树)主席树 orz! ------用于解决区间第k大的问题----xdoj-1216

    poj-2104(区间第K大问题) #include <iostream> #include <algorithm> #include <cstdio> #incl ...

  9. 线段树专题—ZOJ1610 Count the Colors(涂区间,直接tag标记)

    Painting some colored segments on a line, some previously painted segments may be covered by some th ...

随机推荐

  1. DeprecationWarning: Calling an asynchronous function without callback is deprecated. - how to find where the “function:” is?

    I recently updated my node to 7.2.1 and noticed that there is a warning coming: (node:4346) Deprecat ...

  2. 设计模式C++学习笔记之十七(Chain of Responsibility责任链模式)

      17.1.解释 概念:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系.将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止. main(),客户 IWom ...

  3. Cinder模块学习

    理解 Cinder 架构 - 每天5分钟玩转 OpenStack(45)   从本节开始我们学习 OpenStack 的 Block Storage Service,Cinder 理解 Block S ...

  4. html常用标签表单和表格等及css的简单入门

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. jetty去掉项目名称访问

    对于web项目,访问路径是否包含项目名称等修改访问路径的配置方式: 我所使用的是maven进行管理,只需要在pom.xml中进行如下配置 <!-- jetty插件 jetty:run--> ...

  6. linux流量异常查看哪些程序占用的

    Linux下进程/程序网络带宽占用情况查看工具 -- NetHogs   http://www.vpser.net/manage/nethogs.html   来自.  最后略有修改 之前VPS侦探曾 ...

  7. vue打包项目后使用-webkit-line-clamp: 2;这个属性不生效?

    在项目中要实现多行省略,-webkit-line-clamp: 2;打包后不生效,使用下面的方法: word-break: break-all; text-overflow: ellipsis; di ...

  8. Vue1.0到2.0变化

    一.生命周期 二.代码片段 在vue1.0中可以在template编写时出现: <template> <div>第一行</div> <div>第二行&l ...

  9. swift 学习- 21 -- 类型转换

    // 类型转换 可以判断实例的类型, 也可以将实例看做其父类的或者子类的实例 // 类型转换在 Swift 中使用 is 和 as 操作符实现, 这两个操作符提供了一种简单达意的方式去检查值的类型 或 ...

  10. jenkins自动发布java代码

    注:本文来源于<KaliArch> jenkins笔记 一.相关概念 1.1 Jenkins概念: Jenkins是一个功能强大的应用程序,允许持续集成和持续交付项目,无论用的是什么平台. ...