http://blog.csdn.net/liuledidai/article/details/9964697
( 1 )線段樹功能: 單點替換 區間最值
I Hate It
#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 MAX[maxn<<2];
void PushUP(int rt) {
MAX[rt] = max(MAX[rt<<1] , MAX[rt<<1|1]);
}
void build(int l,int r,int rt) { //rt當前節點編號
if (l == r)//l==r,說明rt節點是葉子節點
{ //由于先build左子樹,再build右子樹,所以最終會先寫完左邊的葉子節點,再寫完右邊的葉子節點
scanf("%d",&MAX[rt]);
return ;
}
int m = (l + r) >> 1;
build(l , m , rt << 1);//rt*2
build(m + 1 , r , rt << 1 | 1);//rt*2+1( rt*2 是偶數,rt<<1|1=rt*2+1)
PushUP(rt);
}
void update(int p,int sc,int l,int r,int rt)
{
if (l == r) {
MAX[rt] = sc;
return ;
}
int m = (l + r) >> 1;
if (p <= m) update(p , sc , l , m , rt << 1);
else update(p , sc , m + 1 , r , rt << 1 | 1);
//PushUP(rt);
MAX[rt] = max(MAX[rt<<1] , MAX[rt<<1|1]);
}
int query(int L,int R,int l,int r,int rt)//l r為線段樹的完美區間,l mid為左子樹區間, mid+1,r為右子樹區間
{ //L , R是當前查詢區間,它可能不是完美的,有可能包含左子樹區間的一部分或者右子樹的一部分
if(L<=l&&R>=r) return MAX[rt];
int m = (l + r) >> 1;
int ret = 0;
if(m>=R) return query(L,R,l,m,rt<<1);
else if(m+1<=L) return query(L,R,m+1,r,rt<<1|1);
else return max(query(L,m,l,m,rt<<1),query(m+1,R,m+1,r,rt<<1|1));
}
int main() {
int n , m;
while (~scanf("%d%d",&n,&m)) {
build(1 , n , 1);
while (m --) {
char op[2];
int a , b;
scanf("%s%d%d",op,&a,&b);
if (op[0] == 'Q') printf("%d\n",query(a , b , 1 , n , 1));
else update(a , b , 1 , n , 1);
}
}
return 0;
}
( 2)線段樹功能:update:單點增減 query:區間求和
敵兵布陣
#include <cstdio>
#include <algorithm>
#include<cstring>
#define mid ((l+r)>>1)
using namespace std;
const int MAXN=50010;
int sum[MAXN<<2];
void build(int l,int r,int rt)
{
if(l==r)
{
scanf("%d",&sum[rt]);
return;
}
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void update(int pos,int val,int l,int r,int rt)
{
if(l==r)
{
sum[rt]+=val;
return;
}
if(pos<=mid) update(pos,val,l,mid,rt<<1);
else update(pos,val,mid+1,r,rt<<1|1);
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
int query(int L,int R,int l,int r,int rt)
{
if(L==l&&R==r) return sum[rt];
if(R<=mid) return query(L,R,l,mid,rt<<1);
else if(mid+1<=L) return query(L,R,mid+1,r,rt<<1|1);
else return query(L,mid,l,mid,rt<<1)+query(mid+1,R,mid+1,r,rt<<1|1);
}
int main()
{
int t,n,L,R,val,pos;
char order[10];
scanf("%d",&t);
for(int cas=1;cas<=t;cas++)
{
scanf("%d",&n);
build(1,n,1);
printf("Case %d:\n",cas);
while(true)
{
scanf("%s",order);
if(strcmp(order,"End")==0) break;
else if(strcmp(order,"Query")==0)
{
scanf("%d%d",&L,&R);
printf("%d\n",query(L,R,1,n,1));
}
else if(strcmp(order,"Add")==0)
{
scanf("%d%d",&pos,&val);
update(pos,val,1,n,1);
}
else
{
scanf("%d%d",&pos,&val);
update(pos,-val,1,n,1);
}
}
}
return 0;
}
求逆序數+離散化
( 3 )線段樹功能:update:單點增減 query:區間求和
參考文檔
Ultra-QuickSort
題意:
求解一個序列的逆序數
#include <cstdio>
#include<cstdlib>
#include<string.h>
#include <algorithm>
#define maxn 500010
using namespace std;
int segTree[maxn<<2],aa[maxn];
struct Node
{
int val,id;
}arr[maxn];
int query(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R) return segTree[rt];
int mid=(l+r)>>1;
int ret=0;
if(L<=mid) ret+=query(L,R,l,mid,rt<<1);
if(R>=mid+1) ret+=query(L,R,mid+1,r,(rt<<1)|1);
return ret;
}
void update(int index,int l,int r,int rt)
{
if(l==r)
{
segTree[rt]++;
return;
}
int mid=(l+r)>>1;
if(index<=mid) update(index,l,mid,rt<<1);
else update(index,mid+1,r,(rt<<1)|1);
segTree[rt]=segTree[rt<<1]+segTree[(rt<<1)|1];
}
int cmp(const void *a,const void * b)
{
return (*(Node *)a).val-(*(Node *)b).val;
}
int main() {
int n,i;
while(scanf("%d",&n)!=EOF,n)
{
memset(segTree,0,sizeof(segTree));
for(i=1;i<=n;i++)
{
scanf("%d",&arr[i].val);
arr[i].id=i;
}
qsort(arr+1,n,sizeof(Node),cmp);
for(int i=1;i<=n;i++)
aa[arr[i].id]=i;
long long sum=0;
for(int i=1;i<=n;i++)
{
sum+=query(aa[i],n,1,n,1);
update(aa[i],1,n,1);
}
printf("%lld\n",sum);
}
}
#include <cstdio>
#include<cstdlib>
#include<string.h>
#include <algorithm>
#define maxn 5005
using namespace std;
int segTree[maxn<<2],arr[maxn];
int query(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R) return segTree[rt];
int mid=(l+r)>>1;
int ret=0;
if(L<=mid) ret+=query(L,R,l,mid,rt<<1);
if(R>=mid+1) ret+=query(L,R,mid+1,r,(rt<<1)|1);
return ret;
}
void update(int index,int l,int r,int rt)
{
if(l==r)
{
segTree[rt]++;
return;
}
int mid=(l+r)>>1;
if(index<=mid) update(index,l,mid,rt<<1);
else update(index,mid+1,r,(rt<<1)|1);
segTree[rt]=segTree[rt<<1]+segTree[(rt<<1)|1];
}
int main() {
int n,i;
while(scanf("%d",&n)!=EOF)
{
memset(segTree,0,sizeof(segTree));
for(i=1;i<=n;i++)
{
scanf("%d",&arr[i]);
}
int sum=0;
for(int i=1;i<=n;i++)
{
sum+=query(arr[i],n-1,0,n-1,1);
update(arr[i],0,n-1,1);
}
int ret=sum;
for(int i=1;i<=n;i++)
{
sum+=n-arr[i]-arr[i]-1;
ret=min(sum,ret);
}
printf("%lld\n",ret);
}
}
( 4 ) 題意:h*w的木板,放進一些1*L的物品,求每次放空間能容納且最上邊的位子
思路:每次找到最大值的位子,然后減去L
線段樹功能:query:區間求最大值的位子(直接把update的操作在query里做了)
把每行看成線段樹的葉子記錄每片葉子的剩余空間,而根節點中保存的是葉子節點最大的剩余空間,每次判斷給定的寬度從根節點能否插入,若左子數可以插則優先插入左子樹,這樣就可以保證每次插入都插在最上面。
#include <cstdio>
#include<cstdlib>
#include<string.h>
#include <algorithm>
#define maxn 900010
using namespace std;
int segTree[maxn],w,h,n;
void build(int l,int r,int rt)
{
segTree[rt]=w;
if(l==r) return;
int mid=(l+r)>>1;
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
}
int query(int need,int l,int r,int rt)
{
if(l==r)
{
segTree[rt]-=need;
return l;
}
int ans,mid=(l+r)>>1;
if(segTree[rt<<1]>=need) ans=query(need,l,mid,rt<<1);
else ans=query(need,mid+1,r,rt<<1|1);
segTree[rt]=max(segTree[rt<<1],segTree[rt<<1|1]);
return ans;
}
int main() {
int i,need;
while(scanf("%d%d%d",&h,&w,&n)!=EOF)
{
h=min(h,n);
build(1,h,1);
for(i=1;i<=n;i++)
{
scanf("%d",&need);
if(segTree[1]<need) printf("-1\n");
else printf("%d\n",query(need,1,h,1));
}
}
}
https://scut.online/problem.php?id=77
( 5 ) 題意:有兩個操作:
1、將區間[ L,R ]里的所有數變為原來的一半。
2、詢問區間 [ L,R ]的數之和,結果對1000000007取模。
n,m(n,m≤100000),n為點的個數,編號為1-n,m為操作次數
線段樹功能:區間更新,區間求和。注意一個可以優化的地方:在query和update操作中,如果segTree[rt]==0,則不必遞歸下去了;不然容易超時。
#include<stdio.h>
#include <string.h>
using namespace std;
#define maxn 100010
typedef long long LL;
LL segTree[maxn<<2];
const LL mod=1000000007;
//void pushUp(int rt)
//{
// segTree[rt]=(segTree[rt<<1]+segTree[(rt<<1)|1])%mod;
//}
void build(int l,int r,int rt)
{
if(l==r)
{
scanf("%lld",&segTree[rt]);
return;
}
int mid=l+((r-l)>>1);
build(l,mid,rt<<1);
build(mid+1,r,(rt<<1)|1);
segTree[rt]=(segTree[rt<<1]+segTree[(rt<<1)|1])%mod;
}
LL query(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R) return segTree[rt];
if(L<=l&&r<=R&&segTree[rt]==0) return 0;
int mid=l+((r-l)>>1);
LL sum=0;
if(mid>=L) sum=(sum+query(L,R,l,mid,rt<<1))%mod;
if(mid+1<=R) sum=(sum+query(L,R,mid+1,r,(rt<<1)|1))%mod;
return sum;
}
void update(int L,int R,int l,int r,int rt)
{
if(l==r)
{
segTree[rt]>>=1;
return;
}
if(L<=l&&r<=R&&segTree[rt]==0) return;
int mid=l+((r-l)>>1);
if(L<=mid) update(L,R,l,mid,rt<<1);
if(mid+1<=R) update(L,R,mid+1,r,(rt<<1)|1);
segTree[rt]=(segTree[rt<<1]+segTree[(rt<<1)|1])%mod;
}
int main()
{
int t,L,R;
char op[5];
scanf("%d",&t);
while(t--)
{
int n,m;
scanf("%d%d",&n,&m);
build(1,n,1);
for(int i=1;i<=m;i++)
{
scanf("%s%d%d",op,&L,&R);
if(op[0]=='A')
{
printf("%lld\n",query(L,R,1,n,1));
}
else
{
update(L,R,1,n,1);
}
}
}
}
( 6 ) 線段樹功能:update :區間增減 query:區間求和
題目大意:給定N個數的序列,有Q個操作。
操作分兩種:
1、將某個區間內的所有數都加上某個數。
2、計算某個區間的所有數之和并輸出。
方法一:逐點更新
我們有一個樸素的思路,更新區間時對區間內的所有點逐個更新
#include <cstdio>
#include <algorithm>
#include<cstring>
#define mid ((l+r)>>1)
using namespace std;
const int MAXN=100010;
long long sum[MAXN<<2];
void build(int l,int r,int rt)
{
if(l==r)
{
scanf("%lld",&sum[rt]);
return;
}
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void update(int L,int R,int l,int r,int rt,long long val)
{
if(l==r)//對區間內的所有點逐個更新
{
sum[rt]+=val;
return;
}
if(R<=mid) update(L,R,l,mid,rt<<1,val);
else if(mid+1<=L) update(L,R,mid+1,r,rt<<1|1,val);
else
{
update(L,mid,l,mid,rt<<1,val);
update(mid+1,R,mid+1,r,rt<<1|1,val);
}
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
long long query(int L,int R,int l,int r,int rt)
{
if(L==l&&R==r) return sum[rt];
if(R<=mid) return query(L,R,l,mid,rt<<1);
else if(mid+1<=L) return query(L,R,mid+1,r,rt<<1|1);
return query(L,mid,l,mid,rt<<1)+query(mid+1,R,mid+1,r,rt<<1|1);
}
int main()
{
int n,m,L,R;
long long val;
char op[5];
scanf("%d%d",&n,&m);
build(1,n,1);
for(int i=1;i<=m;i++)
{
scanf("%s",op);
if(op[0]=='Q')
{
scanf("%d%d",&L,&R);
printf("%lld\n",query(L,R,1,n,1));
}
else
{
scanf("%d%d%lld",&L,&R,&val);
update(L,R,1,n,1,val);
}
}
return 0;
}
但是,對每個點逐個進行更新,相當于暴力無疑,這樣會很容易超時的,而且也體現不出線段樹的優勢!!!
所以需要進行延遲更新,延遲更新指等到調用update時或者查詢時才更新節點的孩子的值
方法二 :延遲更新法
參考圖例
A Simple Problem with Integers
#include<cstdio>
using namespace std;
typedef long long LL;
const int maxn = 100010;
#define lson l,md,(rt<<1)
#define rson md+1,r,(rt<<1|1)
#define ls (rt<<1)
#define rs (rt<<1|1)
#define md ((l+r)>>1)
LL sum[maxn << 2], lazy[maxn << 2];
void up(int rt)
{
sum[rt] = sum[ls] + sum[rs];
}
void down(int l, int r, int rt)//延遲標記向下(孩子節點)傳遞
{
if (lazy[rt])
{
sum[ls] += (md - l + 1) * lazy[rt];//更新左孩子節點的val
lazy[ls] += lazy[rt];//延遲標記向左孩子傳遞,以表明我左孩子節點已經更新了,
//但是!!左孩子的子節點還沒更新呢!!!
sum[rs] += (r - (md + 1) + 1) * lazy[rt];//更新右孩子
lazy[rs] += lazy[rt];//延遲標記向右孩子傳遞,以表明我右孩子節點已經更新了,
//但是!!右孩子的子節點還沒更新呢!!!
lazy[rt] = 0;//當前節點已經更新過了,所以取消標記
}
}
void build(int l, int r, int rt)
{
lazy[rt] = 0;//初始化為0
if (l == r)
{
scanf("%lld", &sum[rt]);
return;
}
build(lson);
build(rson);
up(rt);
}
void update(int L, int R, int l, int r, int rt, LL val)
{
if (L<=l&&R>=r)
{
sum[rt] += (r - l + 1) * val;
lazy[rt] += val;
return;
}
down(l, r, rt);//如果當前節點有延遲標記,那么更新一下節點的val以及傳遞延遲標記給孩子節點
if (L <= md) update(L, R, lson, val);
if (R > md) update(L, R, rson, val);
up(rt);
}
LL query(int L, int R, int l, int r, int rt)
{
if (L<=l&&R>=r) return sum[rt];
down(l, r, rt);//如果當前節點有延遲標記,那么更新一下節點的val以及傳遞延遲標記給孩子節點
LL sum = 0;
if (L <= md) sum += query(L, R, lson);
if (R > md) sum += query(L, R, rson);
return sum;
}
int main() {
int n, m, L, R;
LL val;
char op[5];
scanf("%d%d", &n, &m);
build(1, n, 1);
for (int i = 1; i <= m; i++)
{
scanf("%s", op);
if (op[0] == 'Q')
{
scanf("%d%d", &L, &R);
printf("%lld\n", query(L, R, 1, n, 1));
}
else
{
scanf("%d%d%lld", &L, &R, &val);
update(L, R, 1, n, 1, val);
}
}
return 0;
}
方法三:
也是延遲更新但是有點不一樣,具體請看代碼
#include<cstdio>
#include <algorithm>
#define mid ((l+r)>>1)
using namespace std;
const int MAXN=100010;
long long sum[MAXN*4],mark[MAXN*4];
void build(int l,int r,int rt)
{
sum[rt]=mark[rt]=0;
if(l==r)
{
scanf("%lld",&sum[rt]);
return;
}
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void update(int l,int r,int L,int R,int rt,int val)
{
sum[rt]=sum[rt]+(R-L+1)*val;
if(l==L&&R==r)
{
mark[rt]+=val;
return;
}
if(R<=mid) update(l,mid,L,R,rt<<1,val);
else if(L>mid) update(mid+1,r,L,R,rt<<1|1,val);
else
{
update(l,mid,L,mid,rt<<1,val);
update(mid+1,r,mid+1,R,rt<<1|1,val);
}
}
long long query(int l,int r,int rt,int L,int R)
{
if(L==l&&R==r) return sum[rt];
long long ans=(R-L+1)*mark[rt];
if(R<=mid) ans+=query(l,mid,rt<<1,L,R);
else if(L>mid) ans+=query(mid+1,r,rt<<1|1,L,R);
else
{
ans+=query(l,mid,rt<<1,L,mid);
ans+=query(mid+1,r,rt<<1|1,mid+1,R);
}
return ans;
}
int main()
{
int n,m,L,R;
long long val;
char op[5];
scanf("%d%d",&n,&m);
build(1,n,1);
for(int i=1;i<=m;i++)
{
scanf("%s",op);
if(op[0]=='Q')
{
scanf("%d%d",&L,&R);
printf("%lld\n",query(1,n,1,L,R));
}
else
{
scanf("%d%d%lld",&L,&R,&val);
update(1,n,L,R,1,val);
}
}
return 0;
}
( 7 ) 線段樹功能: update:區間替換 ,query:區間求和
Just a Hook
題意:
一段線段由n條小線段組成,每次操作把一個區間的小線段變成金銀銅之一(金的價值為3,銀為2,銅為1),最初可當做全為銅;最后求這條線段的總價值。
#include<cstdio>
using namespace std;
typedef long long LL;
const int maxn=100010;
#define lson l,md,(rt<<1)
#define rson md+1,r,(rt<<1|1)
#define ls (rt<<1)
#define rs (rt<<1|1)
#define md ((l+r)>>1)
LL sum[maxn<<2],lazy[maxn<<2];
void up(int rt)
{
sum[rt]=sum[ls]+sum[rs];
}
void down(int l,int r,int rt)
{
if(lazy[rt])
{
sum[ls]=(md-l+1)*lazy[rt];
lazy[ls]=lazy[rt];
sum[rs]=(r-(md+1)+1)*lazy[rt];
lazy[rs]=lazy[rt];
lazy[rt]=0;
}
}
void build(int l,int r,int rt)
{
lazy[rt]=0;
if(l==r)
{
sum[rt]=1;
return;
}
build(lson);
build(rson);
up(rt);
}
void update(int L,int R,int l,int r,int rt,LL val)
{
if(L==l&&r==R)
{
sum[rt]=(R-L+1)*val;
lazy[rt]=val;
return;
}
down(l,r,rt);
if(md>=R) update(L,R,lson,val);
else if(md+1<=L) update(L,R,rson,val);
else{
update(L,md,lson,val);
update(md+1,R,rson,val);
}
up(rt);
}
LL query(int L,int R,int l,int r,int rt)
{
if(L==l&&r==R) return sum[rt];
down(l,r,rt);
if(md>=R) return query(L,R,lson);
else if(md+1<=L) return query(L,R,rson);
return query(L,md,lson)+query(md+1,R,rson);
}
int main() {
int t,n,m,L,R;
LL val;
scanf("%d",&t);
for(int cas=1;cas<=t;cas++)
{
scanf("%d%d",&n,&m);
build(1,n,1);
while(m--)
{
scanf("%d%d%lld",&L,&R,&val);
update(L,R,1,n,1,val);
}
printf("Case %d: The total value of the hook is %lld.\n",cas,query(1,1,1,1,1));
}
return 0;
}
https://vjudge.net/problem/HDU-1698
( 7 ) 線段樹功能: update:區間變為原來的b次方 ,query:區間乘積
題意:操作1: l ,r ,b : 把k∈[l,r] 的妹子, 能力值 ak變成ak^b
操作2:l , r : 求累乘 ∏(l?k?r)ak
題解:a^k ≡ a^(k mod (p?1)) mod p , p是素數.
#include<cstdio>
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=100020;
const LL mod=1000000007;
#define lson l,md,(rt<<1)
#define rson md+1,r,(rt<<1|1)
#define md ((l+r)>>1)
#define ls (rt<<1)
#define rs (rt<<1|1)
LL sum[maxn<<2],lazy[maxn<<2];
LL pow_mod(LL a,int b)
{
LL res=1;
while(b)
{
if(b&1) res=res*a%mod;
b>>=1;
a=a*a%mod;
}
return res;
}
void up(int rt)
{
sum[rt]=sum[ls]*sum[rs]%mod;
}
void down(int l,int r,int rt)
{
if(lazy[rt]!=1)
{
sum[ls]=pow_mod(sum[ls],lazy[rt]);
lazy[ls]=lazy[ls]*lazy[rt]%(mod-1);
sum[rs]=pow_mod(sum[rs],lazy[rt]);
lazy[rs]=lazy[rs]*lazy[rt]%(mod-1);
lazy[rt]=1;
}
}
void update(int L,int R,int l,int r,int rt,int b)
{
if(L==l&&r==R)
{
sum[rt]=pow_mod(sum[rt],b);
lazy[rt]=lazy[rt]*b%(mod-1);
return;
}
down(l,r,rt);
if(R<=md) update(L,R,lson,b);
else if(L>md) update(L,R,rson,b);
else{
update(L,md,lson,b);
update(md+1,R,rson,b);
}
sum[rt]=sum[ls]*sum[rs]%mod;
}
LL query(int L,int R,int l,int r,int rt)
{
if(L==l&&r==R) return sum[rt];
down(l,r,rt);
if(R<=md) return query(L,R,lson);
else if(L>md) return query(L,R,rson);
return query(L,md,lson)*query(md+1,R,rson)%mod;
}
void build(int l,int r,int rt)
{
lazy[rt]=1;
if(l==r)
{
scanf("%lld",&sum[rt]);
return ;
}
build(lson);
build(rson);
sum[rt]=sum[ls]*sum[rs]%mod;
}
int main() {
int t,n,m;
scanf("%d", &t);
while(t--) {
scanf("%d%d", &n, &m);
build(1, n, 1);
while(m--) {
int op, l, r, b;
scanf("%d%d%d", &op, &l, &r);
if(op == 1) {
scanf("%d", &b);
update(l,r,1,n,1,b);
}
else {
LL ans = query(l,r,1,n,1);
printf("%lld\n", ans);
}
}
}
return 0;
}