【例题】【线段树】

来源:互联网 发布:java instanceof 编辑:程序博客网 时间:2024/05/29 19:13

1、
!!!!!注意讨论不能建树的情况
NKOJ 1321 数列操作问题
时间限制 : 10000 MS 空间限制 : 165536 KB

问题描述
假设有一列数{Ai}(1≤i≤n),支持如下两种操作:
将Ak的值加D。(k, D是输入的数)
输出As+As+1+…+At。(s, t都是输入的数,S≤T)

输入格式
第一行一个整数n,
第二行为n个整数,表示{Ai}的初始值≤10000。
第三行为一个整数m,表示操作数
下接m行,每行描述一个操作,有如下两种情况:
ADD k d (表示将Ak加d,1<=k<=n,d为数,d的绝对值不超过10000)
SUM s t (表示输出As+…+At)

输出格式
对于每一个SUM提问,输出结果

样例输入
5
1 2 3 2 4
5
SUM 1 2
SUM 1 5
ADD 1 2
SUM 1 2
SUM 1 5

样例输出
3
12
5
14

提示
M,N<=100000

/*
1、线段树是通过二分思想建立的一颗表示区间关系的树形结构.
2、节点数最多2n-1个
*/

#include<cstdio>#include<iostream>using namespace std;const int need=100003;struct fy{int a,b,le,ri,val;}tree[need*2];//记录左右儿子就开2×n//也可以{int a,b,val;}tree[need*4]i*2表示左儿子,i*2+1表示右儿子;不记左右儿子就开4×nint a[need];int tot=0,k,d;void build(int x,int y){    int s=++tot;    tree[s].a=x,tree[s].b=y;    //tree[s].val=suma[y]-suma[x-1];    if(x<y)    {        tree[s].le=tot+1;        build(x,(x+y)/2);        tree[s].ri=tot+1;        build((x+y)/2+1,y);        tree[s].val=tree[tree[s].le].val+tree[tree[s].ri].val;    }    else if(x==y) tree[s].val=a[x];}void add_(int s){    tree[s].val+=d;    if(tree[s].le!=0&&tree[tree[s].le].a<=k&&tree[tree[s].le].b>=k) add_(tree[s].le);    else if(tree[s].ri!=0&&tree[tree[s].ri].a<=k&&tree[tree[s].ri].b>=k) add_(tree[s].ri);}int sum_(int s){    if(k<=tree[s].a&&tree[s].b<=d) return tree[s].val;    int ans=0;    if(!(tree[tree[s].le].a>d||tree[tree[s].le].b<k)) ans+=sum_(tree[s].le);    if(!(tree[tree[s].ri].a>d||tree[tree[s].ri].b<k)) ans+=sum_(tree[s].ri);    /*或:    if(k<=tree[tree[s].le].b&&tree[tree[s].le].a<=d) ans+=sum_(tree[s].le);    if(k<=tree[tree[s].ri].b&&tree[tree[s].ri].a<=d) ans+=sum_(tree[s].ri);    也可以判断是否在区间内    */    return ans;}/*再或int sum_(int s){    if(d<tree[s].a||k>tree[s].b) return 0;    if(k<=tree[s].a&&tree[s].b<=d) return tree[s].val;    return sum_(s<<1)+sum_((s<<1)|1);}*/int main(){    int n;scanf("%d",&n);    //int a;    for(int i=1;i<=n;i++)    {        scanf("%d",&a[i]);        //suma[i]=suma[i-1]+a;     }     build(1,n);    int m;scanf("%d",&m);    string b;    for(int i=1;i<=m;i++)    {        cin>>b;scanf("%d%d",&k,&d);        if(b=="SUM")  printf("%d\n",sum_(1));        else if(b=="ADD") add_(1);    }} 

2、
NKOJ 1344 人力资源管理
时间限制 : 10000 MS 空间限制 : 65536 KB
问题描述
某公司有n个员工,每个员工有一个工作能力值(该值为60000以内的自然数)。
Tom是公司人力资源部门的主管,他可以进行如下3种操作:
1.Tom为公司招聘了一个能力值为x的新员工
2.Tom为公司辞退了一个能力值为y的员工
3.Tom要查出在所有员工能力值由高到低的排名中,能力值大于W的员工的人数

输入格式
第一行两个整数n,m 表示有n个员工,和tom的m项工作
接下来一行,n个整数,表示n个员工的能力值
接下来m行,每行有两个整数a,b(1<=b<=60000)。
a==1 表示招聘了一个能力值为b的新员工
a==2 表示辞退了一个能力值为b的员工(若没有能力值为b的员工,则该操作无效)
a==3 表示查出能力值>b的员工的个数,并输出结果

输出格式
若干行,每行一个整数,表示输入中所有a==3的操作的结果。

样例输入
样例输入1:
6 5
9 4 6 2 3 5
1 7
1 10
3 6
2 9
3 6
样例输入2:
4 4
4 1 2 5
1 3
3 3
2 4
3 1
样例输出
样例输出1:
3
2
样例输出2:
2
3
提示
n,m<=100000

思路:
1、每个员工的能力值在60000以内,所以建立一个表示区间[1,60001]的线段树
2、对于第三类查询,即为求b+1到max区间中的人数,注意当b+1大于max时直接输出0;

#include<iostream>#include<cstdio>using namespace std;const int need1=60003;const int need2=100003;struct fy{int a,b,le,ri,val;}t[need2*2];struct yf{int mold,num;}c[need2];int a[need2];int tot=0,k,ne=-need1;void build(int x,int y){    int s=++tot;    t[s].a=x,t[s].b=y;    if(x==y)    {        t[s].val=a[x];        return ;    }    t[s].le=tot+1;build(x,(x+y)/2);    t[s].ri=tot+1;build((x+y)/2+1,y);    t[s].val=t[t[s].le].val+t[t[s].ri].val;}void add_(int s){     if(t[s].a<=k&&k<=t[s].b) t[s].val++;     if(t[s].le!=0&&t[t[s].le].a<=k&&k<=t[t[s].le].b) add_(t[s].le);     else if(t[s].ri!=0&&t[t[s].ri].a<=k&&k<=t[t[s].ri].b) add_(t[s].ri);} void fire_(int s){    if(t[s].a<=k&&k<=t[s].b) t[s].val--;    if(t[s].le!=0&&t[t[s].le].a<=k&&k<=t[t[s].le].b) fire_(t[s].le);    else if(t[s].ri!=0&&t[t[s].ri].a<=k&&k<=t[t[s].ri].b) fire_(t[s].ri);}int cnt(int s){    if(k+1<=t[s].a&&t[s].b<=ne) return t[s].val;    int ans=0,mid=(t[s].a+t[s].b)/2;    if(k<mid) ans+=cnt(t[s].le);    ans+=cnt(t[s].ri);    return ans; }int main(){    int n,m;scanf("%d%d",&n,&m);    for(int b,i=1;i<=n;i++)    {        scanf("%d",&b);        if(ne<b) ne=b;        a[b]++;    }    for(int i=1;i<=m;i++)    {        scanf("%d%d",&c[i].mold,&c[i].num);        if(c[i].num>ne&&c[i].mold==1) ne=c[i].num;    }     build(1,ne);    for(int i=1;i<=m;i++)    {        k=c[i].num;        if(c[i].mold==2&&a[k])        {            a[k]--;            fire_(1);        }         else if(c[i].mold==1)         {            a[k]++;            add_(1);        }        else if(c[i].mold==3) printf("%d\n",k>=ne?0:cnt(1));    }} 
0 0
原创粉丝点击