线段树 单点更新 【第一节】

风格:notonlysuccess

感觉刚开始就看notonlysuccess可能不太好,建议还是先研究一下网上的其他风格,搞透之后在比较一下,这样就能充分理解并加以运用了

下面就从一些题来初识一下线段树

hdu 1394 Minimum Inversion Number

求逆序数的功能

View Code
#include<cstdio>
#include<algorithm>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn = 5010;
int sum[maxn<<2];
void Pushup(int rt){
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
void build(int l,int r,int rt){
sum[rt] = 0;
if (l == r) return ;
int m = (l + r) >> 1 ;
build(lson);
build(rson);
}
void update(int p,int l,int r,int rt){
if(l == r) {
sum[rt]++;
return;
}
int m = (l + r) >> 1;
if(p <= m) update(p , lson);
else update(p , rson);
Pushup(rt);
}
int query(int L,int R,int l,int r,int rt){
if(L <= l && r <= R){
return sum[rt];
}
int m = (l + r) >> 1;
int ret = 0;
if(L <= m) ret += query(L , R , lson);
if(R > m) ret += query(L , R , rson);
return ret;
}
int min(int a,int b){
return a<b?a:b;
}
int val[maxn];
int main(){
int n,i;
while(~scanf("%d",&n)){
build(0 , n-1 , 1);
int sum=0;
for(i = 0 ; i < n ; i++){
scanf("%d",&val[i]);
sum += query( val[i] , n-1 , 0 , n-1 , 1 );//下标从0开始的
update( val[i] , 0 , n-1 , 1);
}
int ret=sum;
for(i=0;i<n;i++)
{
sum+=n-val[i]-val[i]-1;
ret=min(ret , sum);
}
printf("%d\n",ret);
}
return 0;
}


也附上树状数组的

View Code
#include<stdio.h>
#include<string.h>
const int MAX = 5010;
int c[MAX];
int lowbit(int x){
return x&(-x);
}
void update(int x,int d){
while(x<=MAX){
c[x]+=d;
x+=lowbit(x);
}
}
int sum(int x){
int ret=0;
while(x>0){
ret+=c[x];
x-=lowbit(x);
}
return ret;
}
int min(int a,int b){
return a<b?a:b;
}
int val[MAX];
int main(){
int n,i,j,k;
while(~scanf("%d",&n)){
int ans=0;memset(c,0,sizeof(c));
for(i=1;i<=n;i++){
scanf("%d",&val[i]);
val[i]++;
ans+=i-1-sum(val[i]);
update(val[i],1);
}
int ret=ans;
for(i=1;i<=n;i++){
ans+=n-val[i]-val[i]+1;
ret=min(ret,ans);
}
printf("%d\n",ret);
}
return 0;
}

 

 

hdu 1166 敌兵布阵

单点增减,区间求和

View Code
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 55555;
#define mid (l+r)>>1
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
int sum[maxn<<2];
void Pushup(int rt){
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void build(int l,int r,int rt){
if(l==r){
scanf("%d",&sum[rt]);
return ;
}
int m = mid;
build(lson);
build(rson);
Pushup(rt);
}
void update(int p,int add,int l,int r,int rt){
if(l==r){
sum[rt]+=add;
return ;
}
int m = mid;
if(p <= m) update(p , add , lson);
else update(p , add , rson);
Pushup(rt);
}
int query(int L,int R,int l,int r,int rt){
if(L <= l && r <= R ){
return sum[rt];
}
int m = mid;
int ret = 0;
if(L <= m) ret += query(L, R , lson);
if(R > m) ret += query(L, R , rson);
return ret;
}
int main(){
int t,n,cases=1,a,b;
scanf("%d",&t);
while(t--){
printf("Case %d:\n",cases++);
scanf("%d",&n);
build(1 , n ,1);
char s[10];
while(~scanf("%s",s)){
if(s[0] == 'E') break;
scanf("%d%d",&a , &b ) ;
if(s[0] == 'Q') printf("%d\n",query(a , b , 1 , n , 1));
else if(s[0] == 'S') update(a , -b , 1 , n ,1);
else update(a , b , 1 , n, 1);
}
}
return 0;
}

 

 

hdu 1754 I hate it

单点修改,区间最值

View Code
#include<cstdio>
#include<algorithm>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn = 222222;
int M[maxn<<2];
int max(int a,int b){
return a>b?a:b;
}
void Pushup(int rt){
M[rt]=max(M[rt<<1] , M[rt<<1|1]);
}
void build(int l,int r,int rt){
if(l == r) {
scanf("%d",&M[rt]);
return ;
}
int m = (l+r) >> 1;
build(lson);
build(rson);
Pushup(rt);
}
void update(int p,int ch,int l,int r,int rt){
if(l==r){
M[rt] = ch;
return ;
}
int m = (l+r) >> 1;
if(p <= m) update(p , ch , lson);
else update(p , ch , rson);
Pushup(rt);
}
int query(int L,int R,int l,int r,int rt){
if(L <= l && r <= R){
return M[rt];
}
int m = (l+r) >> 1;
int ret=0;
if(L <= m) ret = max(ret , query(L, R , lson));
if(R > m) ret = max(ret , query(L, R , rson));
return ret;
}
int main(){
int n , m;
while(~scanf("%d%d",&n,&m)){
build(1 , n , 1);
while(m--){
char s[5];
int a , b;
scanf("%s%d%d",s,&a,&b);
if(s[0] == 'Q') printf("%d\n",query(a , b , 1 , n , 1));
else update(a , b , 1 , n ,1);
}
}
return 0;
}



hdu 2795 Billboard

h*w 的 木板,放进一些1*L的物品,求每次放空间能容纳且最上面的位子
每次找能容纳海报且最上面的位置,再减去x
每次询问的时候直接更新了,也就不用写update了

View Code
#include<cstdio>
#include<algorithm>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define mid (l+r) >> 1
const int maxn = 300000;
int M[maxn<<2];
int h,w,n;
int max(int a,int b){
return a>b?a:b;
}
void Pushup(int rt){
M[rt]=max(M[rt<<1] , M[rt<<1|1]);
}
void build(int l,int r,int rt){
M[rt] = w;//
if(l == r) return ;
int m = mid;
build(lson);
build(rson);
}
int query(int x,int l,int r,int rt){
if(l == r){
M[rt] -= x;
return l;
}
int m = mid;
int ret = (M[rt<<1] >= x) ? query(x , lson) : query(x , rson);
Pushup(rt);
return ret;
}
int main(){
int n , m;
while(~scanf("%d%d%d",&h,&w,&n)){
if(h > n) h = n;
build(1 , h , 1);
while(n--){
int x;
scanf("%d",&x);
if(M[1] < x) printf("-1\n");
else printf("%d\n",query(x , 1 , h , 1));
}
}
return 0;
}

 

 

poj 2828 Buy tickets

结点存储下面有几个空位
每次从根结点往下找找到该插入的位置,同时更新每个节点的值
注:其实有时候 debug 的过程才是一个程序最重要的过程 debug往往比写出整体的代码要难

View Code
#include<cstdio>
#include<algorithm>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define mid (l+r) >> 1
const int maxn = 300000;
int M[maxn<<2];
int id;
int ans[maxn],pos[maxn],val[maxn];
int max(int a,int b){
return a>b?a:b;
}
void build(int l,int r,int rt){
M[rt] = r-l+1;//
if(l == r) return ;
int m = mid;
build(lson);
build(rson);
}
void update(int p,int l,int r,int rt){
M[rt]--;//printf("M[%d]=%d\n",rt,M[rt]);
if(l == r){
id=l;
return ;
}
int m = mid;
if(M[rt<<1] >= p) update(p,lson);
else
{
p-=M[rt<<1];
update(p,rson);
}
}
int main(){
int n , i;
while(~scanf("%d",&n)){
build(1 , n , 1);
for(i=1;i<=n;i++) scanf("%d%d",&pos[i],&val[i]);
for(i=n;i>=1;i--){
// printf("insert %d\n",pos[i]+1);
update(pos[i]+1,1,n,1);
// printf("id=%d\n",id);
ans[id]=val[i];
}
for(i=1;i<=n;i++) printf("%d%c",ans[i],i==n?'\n':' ');
}
return 0;
}

 

树状数组做法

添加时..譬如添加的ki=5..那么他的位置就是从第一个开始..数到第6个空的位置(没被占的位置)..然后填上这个数..这个位置标记已占..而且这个数的位置就确定了..不再动..
这是最朴素的思路..加上树状数组..那就是优化查找该点位置的查找..初始化..a[1..n]都为1..则每次对c[i]求和的就是到该点前有多少个空位..
那如何通过c数组来找到我们需要的位置呢?...使用二分查找的方法..譬如现在要插入的这个人为(ki,xi)..那么我们就二分查找(ki--n)这个范围..因为他的位置至少不会小于ki..

View Code
添加时..譬如添加的ki=5..那么他的位置就是从第一个开始..数到第6个空的位置(没被占的位置)..然后填上这个数..这个位置标记已占..而且这个数的位置就确定了..不再动..
这是最朴素的思路..加上树状数组..那就是优化查找该点位置的查找..初始化..a[1..n]都为1..则每次对c[i]求和的就是到该点前有多少个空位..
那如何通过c数组来找到我们需要的位置呢?...使用二分查找的方法..譬如现在要插入的这个人为(ki,xi)..那么我们就二分查找(ki--n)这个范围..因为他的位置至少不会小于ki..
#include <cstdio>
#include <string.h>
const int N = 200005;
int _n;
int res[N];
int c[N];
struct Peo {
int pos, val;
}peo[N];
int lowbit( int n ){
return n & (-n);
}
void modify( int n, int delta ){
while( n <= _n ){
c[n] += delta;
n += lowbit(n);
}
}
int sum( int n ){
int ret = 0;
while( n != 0 ){
ret += c[n];
n -= lowbit(n);
}
return ret;
}
int getRank( int pos ) {
int mid, begin = pos, end = _n;
while ( begin < end ) {
mid = (begin + end) / 2;
if ( sum(mid) >= pos ) end = mid;
else begin = mid + 1;
}
return end;
}
int main()
{
while ( scanf("%d", &_n) != EOF ) {
int i;
memset(c, 0, sizeof(c));
for (i=1; i<=_n; ++i) {
scanf("%d %d", &peo[i].pos, &peo[i].val);
peo[i].pos++;
modify(i, 1);
}
for (i=_n; i>=1; --i) {
int now = getRank(peo[i].pos);
res[now] = peo[i].val;
modify(now, -1);
}
for (i=1; i<_n; ++i) {
if (i == 1) printf("%d", res[i]);
else printf(" %d", res[i]);
}
printf(" %d\n", res[_n]);
}
return 0;
}




hdu 1556 color the ball
直接从上往下把相应结点被覆盖的次数相加就是下面各点被覆盖的次数

View Code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn = 111111;
int cnt[maxn<<2];
void update(int L,int R,int l,int r,int rt){
if(L <= l && r <= R) {
cnt[rt]++;
return ;
}
int m = (l+r) >> 1;
if(L <= m) update(L, R , lson);
if(m < R) update(L , R , rson);
}
void query(int l,int r,int rt,int sum){
if(l==r) {
if(l==1) printf("%d",sum+cnt[rt]);
else printf(" %d",sum+cnt[rt]);
return ;
}
int m = (l+r)>>1;
query(lson,sum+cnt[rt]);
query(rson,sum+cnt[rt]);
}
int main(){
int a,i,b,n;
while(scanf("%d",&n),n){
memset(cnt,0,sizeof(cnt));
for(i=1;i<=n;i++){
scanf("%d%d",&a,&b);
update(a,b,1,n,1);
}
query(1,n,1,0);
printf("\n");
}
}


树状数组

View Code
#include<stdio.h>
#include<string.h>
int n;
int c[100001];
int lowbit(int x){
return x&(-x);
}
int get_sum(int x){
int sum=0;
while(x>0){
sum+=c[x];
x-=lowbit(x);
}
return sum;
}
void add(int x,int val){
while(x<=n){
c[x]+=val;
x+=lowbit(x);
}
}
int main(){
int a,b,i;
while(scanf("%d",&n)!=-1&&n){
memset(c,0,sizeof(c));
for(i=1;i<=n;i++){
scanf("%d%d",&a,&b);
add(a,1);
add(b+1,-1);
}
printf("%d",get_sum(1));
for(i=2;i<=n;i++)
printf(" %d",get_sum(i));
printf("\n");
}
return 0;
}




hdu   3564   Another LIS

蛮难想的题目,先确定最后一个数的位置,依次往前确认,线段树记录区间内共有几个空位

所有的数都插入到给定的位置后,从1到n求出每个数插入后以当前数结尾的最长不下降子序列

View Code
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn = 111111;
int sum[maxn<<2];
int ans[maxn],dp[maxn],pos[maxn],val[maxn];
void build(int l,int r,int rt){
sum[rt]=r-l+1;
if(l==r) return ;
int m=(l+r)>>1;
build(lson);
build(rson);
}
int query(int l,int r,int rt,int num){
if(l==r) return l;
int m=(l+r)>>1;
if(sum[rt<<1]<num) query(rson,num-sum[rt<<1]);
else query(lson,num);
}
void update(int l,int r,int rt,int x){
sum[rt]--;
if(l==r) return ;
int m=(l+r)>>1;
if(x<=m) update(lson,x);
else update(rson,x);
}
int main(){
int t,cases=1,i,j,n;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
build(1,n,1);
for(i=1;i<=n;i++) scanf("%d",&val[i]);
for(i=n;i>=1;i--){
int index=query(1,n,1,val[i]+1);//找第val【i】+1个空位
pos[index]=i;
update(1,n,1,index);
}
dp[0]=0;
int l=0,r=0,mid,temp,len=0;
for(i=1;i<=n;i++){
temp=pos[i];
l=0,r=len;
while(l<=r){
mid=(l+r)>>1;
if(temp>dp[mid]) l=mid+1;
else r=mid-1;
}
dp[l]=temp;//temp应该插入的位置
ans[temp]=l;//以temp结尾的最长不下降序列的长度
if(l>len) len++;
}
printf("Case #%d:\n",cases++);
ans[0]=0;
for(i=1;i<=n;i++) {
if(ans[i]<ans[i-1]) ans[i]=ans[i-1];
printf("%d\n",ans[i]);
}
printf("\n");
}
return 0;
}

 

 

poj 2182  hdu 2711 lost cows

从后往前插入线段树,线段树维护区间内空位的个数

每次插入第a[i]+1个空位即可,别忘了最后还有一个空位要插

View Code
#include<cstdio>
#include<cstring>
const int maxn = 8000;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
int sum[maxn<<2];
void build(int l,int r,int rt){
sum[rt]=(r-l+1);
if(l==r) return;
int m=(l+r)>>1;
build(lson);
build(rson);
}
void update(int p,int l,int r,int rt,int &pos){
sum[rt]--;
if(l==r){
pos=l;
return ;
}
int m=(l+r)>>1;
if(sum[rt<<1]>=p) update(p,lson,pos);
else {
p-=sum[rt<<1];
update(p,rson,pos);
}
}
int a[8000];
int main()
{
int n,i;
while(scanf("%d",&n)!=EOF){
build(1,n,1);
for(i=1;i<n;i++)
scanf("%d",&a[i]);
int pos;
for(i=n-1;i>=1;i--){
update(a[i]+1,1,n,1,pos);
a[i]=pos;
}
update(1,1,n,1,pos);
a[0]=pos;
for(i=0;i<n;i++)
printf("%d\n",a[i]);
}
}




你可能感兴趣的:(线段树)