zoukankan      html  css  js  c++  java
  • HIT暑期集训 树状数组与线段树

    线段树模板,以POJ 3468为例

    #include<cstdio> 
    #define maxn 100005
    typedef long long ll;
    struct node
    {
        int l,r;
        ll f,w;
    }tree[maxn<<2];
    ll a[maxn],ans;
    void build(int l,int r,int k) 
    {
        tree[k].l=l;tree[k].r=r;
        if (l==r)
        {
            tree[k].w=a[l];
            return;
        }
        int mid=(l+r)>>1;
        build(l,mid,k<<1);
        build(mid+1,r,k<<1|1);
        tree[k].w=tree[k<<1].w+tree[k<<1|1].w;
    }
    void pushdown(int k)
    {
        tree[k<<1].f+=tree[k].f;
        tree[k<<1|1].f+=tree[k].f;
        tree[k<<1].w+=tree[k].f*(tree[k<<1].r-tree[k<<1].l+1);
        tree[k<<1|1].w+=tree[k].f*(tree[k<<1|1].r-tree[k<<1|1].l+1);
        tree[k].f=0;
    }
    void add(int l,int r,int k,ll x)
    {
        if (l<=tree[k].l && tree[k].r<=r)
        {
            tree[k].w+=(tree[k].r-tree[k].l+1)*x;
            tree[k].f+=x;
            return;
        }
        if (tree[k].f) pushdown(k);
        int mid=(tree[k].l+tree[k].r)>>1;
        if (l<=mid) add(l,r,k<<1,x);
        if (r>mid) add(l,r,k<<1|1,x);
        tree[k].w=tree[k<<1].w+tree[k<<1|1].w;
    }
    ll sum(int l,int r,int k)
    {
        if (l<=tree[k].l && tree[k].r<=r)
        {
            return tree[k].w;
        }
        if (tree[k].f) pushdown(k);
        int mid=(tree[k].l+tree[k].r)>>1;
        ll re=0;
        if (l<=mid) re+=sum(l,r,k<<1);
        if (r>mid) re+=sum(l,r,k<<1|1);
        return re;
    }
    int main()
    {
        int i,n,q,x,y;
        ll z;
        char op[5];
        scanf("%d%d",&n,&q);
        for (i=1;i<=n;i++) scanf("%lld",&a[i]);
        build(1,n,1);
        while (q--)
        {
            scanf("%s",op);
            if (op[0]=='Q')
            {
                scanf("%d%d",&x,&y);
                printf("%lld
    ",sum(x,y,1));
            }
            else 
            {
                scanf("%d%d%lld",&x,&y,&z);
                add(x,y,1,z);
            }
        }//= =
        return 0;
    }
    线段树模板

    B    POJ 2155

    题目为区间修改单点查询,应想到树状数组。所以使用二维树状数组。

    #include<cstdio>
    #include<cstring> 
    #define maxn 1005
    using namespace std;
    int c[maxn][maxn],n;
    int lowbit(int x){return x&(-x);}
    void add(int x,int y,int v)
    {  
        int i,j;
        for (i=x;i<=n;i+=lowbit(i)) 
        for (j=y;j<=n;j+=lowbit(j))
            c[i][j]+=v;
    } 
    int getsum(int x,int y)
    {     
        int i,j,re=0;
        for (i=x;i>=1;i-=lowbit(i)) 
        for (j=y;j>=1;j-=lowbit(j))
            re+=c[i][j];
        return re;
    }
    int main()
    {
        int i,t,q;
        int sx,sy,tx,ty;
        char op[20];
        scanf("%d",&t);
        while (t--)
        {
            memset(c,0,sizeof(c));
            scanf("%d%d",&n,&q);
            while (q--)
            {
                scanf("%s",op);
                if (op[0]=='C')
                {
                    scanf("%d%d%d%d",&sx,&sy,&tx,&ty);
                    add(sx,sy,1);
                    add(tx+1,sy,-1);
                    add(sx,ty+1,-1);
                    add(tx+1,ty+1,1);
                }
                else 
                {
                    scanf("%d%d",&sx,&sy);
                    printf("%d
    ",getsum(sx,sy)%2);
                }
            }
            printf("
    ");
        }
        return 0;
    }
    View Code

     D    HDU 3450

    题意:给一个长度为n的序列,定义完美子序列满足长度不小于2且相邻的两个元素之差的绝对值不大于d,问该序列有多少完美子序列。

    思路:首先可以想到一个DP做法,设dp[i]表示以第i位结尾的完美子序列数,

    则有dp[i]=∑(dp[j]+1),j<i且|a[i]-a[j]|<=d,ans=∑dp[i]

    DP做法复杂度为n2,不可行(n<=1e5)

    考虑使用树状数组。先复制一个数组b,将其排序。这样就可以二分查找出|a[i]-a[j]|<=d在b数组中的最左端l和最右端r,

    可用树状数组维护dp[i]+1,于是∑(dp[j]+1)=getsum(r)-getsum(l-1)。

    (注意,因为要求字串长度不小于2,初始的所有位dp[i]+1=0)

    更新当前的dp[i]+1=∑(dp[j]+1)+1,ans=ans+dp[i]=ans+∑(dp[j]+1)。

    #include<cstdio> 
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    const int mod=9901;
    const int maxn=100005;
    int a[maxn],b[maxn],c[maxn],n;
    int lowbit(int x){return x&(-x);}
    void add(int x,int v)
    {  
        int i;
        for (i=x;i<=n;i+=lowbit(i)) c[i]=(c[i]+v)%mod;
    } 
    int getsum(int x)
    {     
        int i,re=0;
        for (i=x;i;i-=lowbit(i)) re=(re+c[i])%mod;
        return re;
    }
    int main()
    {
        int d,i,ans;
        while (scanf("%d%d",&n,&d)!=EOF)
        {
            ans=0;
            memset(c,0,sizeof(c));
            for (i=1;i<=n;i++) 
            {
                scanf("%d",&a[i]);
                b[i]=a[i];
            }
            sort(b+1,b+n+1);
            for (i=1;i<=n;i++)
            {
                int l=lower_bound(b+1,b+n+1,a[i]-d)-b;
                int r=lower_bound(b+1,b+n+1,a[i]+d+1)-b;
                int t=lower_bound(b+1,b+n+1,a[i])-b;
                int x=(getsum(r-1)-getsum(l-1)+mod)%mod;
                add(t,x+1);
                ans=(ans+x)%mod;
            }
            printf("%d
    ",ans);
        }
        return 0;
     } 
    View Code

    F    HDU 3333

    题意:给一个长度为n的序列,进行q次询问,每次询问区间[l,r]中不同大小数字的和。

    思路:离线+树状数组+map。将q次询问按照r排序。

    对于每一个数,利用map来记录前一个大小相同的数的位置。

    每次将r前面未加入树状数组的数加入树状数组,从左至右依次加入。

    如果发现之前有大小相同的数已经加入,就将其删去。

    这样就能保证树状数组中每个大小不同的数只出现一次且出现的位置尽量靠右。

    #include<cstdio> 
    #include<cstring>
    #include<algorithm>
    #include<map> 
    using namespace std;
    typedef long long ll;
    const int maxn=30005;
    const int maxq=100005;
    map<int,int>mp;
    int n,a[maxn],pre[maxn];
    ll c[maxn],ans[maxq];
    struct qu
    {
        int l,r,id;
        bool operator < (const qu &x)const
        {
            if (r==x.r) return l<x.l;
            return r<x.r;
        }
    }q[maxq];
    int lowbit(int x){return x&(-x);}
    void add(int x,int v)
    {  
        for (int i=x;i<=n;i+=lowbit(i)) c[i]+=v;
    } 
    ll getsum(int x)
    {     
        ll re=0;
        for (int i=x;i;i-=lowbit(i)) re+=c[i];
        return re;
    }
    int main()
    {
        int i,j,T,Q;
        scanf("%d",&T);
        while (T--)
        {
            memset(c,0,sizeof(c));
            mp.clear();
            scanf("%d",&n);
            for (i=1;i<=n;i++) 
            {
                scanf("%d",&a[i]);
                pre[i]=mp[a[i]];
                mp[a[i]]=i;
            }
            scanf("%d",&Q);
            for (i=1;i<=Q;i++) 
            {
                scanf("%d%d",&q[i].l,&q[i].r);
                q[i].id=i;
            }
            sort(q+1,q+Q+1);
            j=1;
            for (i=1;i<=Q;i++)
            {
                for (;j<=n && j<=q[i].r;j++)
                {
                    if (pre[j]) add(pre[j],-a[j]);
                    add(j,a[j]);
                }
                ans[q[i].id]=getsum(q[i].r)-getsum(q[i].l-1);
            }
            for (i=1;i<=Q;i++) printf("%lld
    ",ans[i]);
        }
        return 0;
    } 
    View Code

    G    HDU 4578

    H    HDU 2795

    把每一行看作一个节点,行的长度看作节点的值,就能得到一个长度为h的序列。用线段树维护这个序列的区间最大值

    每次操作时选取比x大的最小的行,并且更新行的长度

    #include<cstdio> 
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define maxn 200005
    using namespace std;
    typedef long long ll;
    struct node
    {
        int l,r,w;
    }tree[maxn<<2];
    void build(int l,int r,int k,int w) 
    {
        tree[k].l=l;tree[k].r=r;
        tree[k].w=w;
        if (l==r) return;
        int mid=(l+r)>>1;
        build(l,mid,k<<1,w);
        build(mid+1,r,k<<1|1,w);
        tree[k].w=max(tree[k<<1].w,tree[k<<1|1].w);
    }
    void update(int x,int k)
    {
        if (tree[k].l==tree[k].r)
        {
            printf("%d
    ",tree[k].l);
            tree[k].w-=x;
            return;
        }
        if (x<=tree[k<<1].w) update(x,k<<1);
        else update(x,k<<1|1);
        tree[k].w=max(tree[k<<1].w,tree[k<<1|1].w);
    }
    int main()
    {
        int h,w,n,i,x;
        while (scanf("%d%d%d",&h,&w,&n)!=EOF)
        {
            h=min(h,n);
            build(1,h,1,w);
            for (i=1;i<=n;i++)     
            {
                scanf("%d",&x);
                if (tree[1].w>=x) update(x,1);
                else printf("-1
    ");
            }
        }
        return 0;
     } 
    View Code

    I    HDU 6054

    J    HDU 6315

  • 相关阅读:
    Eclipse给方法添加注释
    隐式等待和显示等待和固定等待
    用例执行完后切换到指定的页面
    HTTP常见响应状态码
    Java类初始化顺序问题
    博客园!我来了
    MySQL的存储引擎
    String声明为NULL和""的区别
    Mysql入门
    MySQL查询与结构
  • 原文地址:https://www.cnblogs.com/lsykk/p/13441211.html
Copyright © 2011-2022 走看看