zoukankan      html  css  js  c++  java
  • URAL1523(dp+树状数组)

    题目链接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=41224#problem/B

    分析:可以设dp[i][j]表示以i结尾长度为j的子序列的个数,那么更新就是dp[i][j]=∑dp[k][j-1],其中k<i,而且a[k]>a[i]。而要更新dp值,可以用树状数组维护,按顺序插入序列值,那么树状数组的值就可以表示比它小的长度为j-1的所有子序列的和,这样就可以在logn的时间更新dp值了,所以总复杂度是O(n*k*logn)

    树状数组有两种用途(以一维树状数组举例):   

    1.单点更新,区间查询(即求和)         

    单点更新时,是往树根(即c[n])拓展         

    而区间查询时,是往叶子节点(即c[1])拓展   

    2.区间更新,单点查询         

    区间更新时,是往叶子节点(即c[1])拓展         

    单点查询时,往树根(即c[n])拓展

    这两个操作只不过是在update()和sum()方法中的+和-替换一下而已。

    下面解释一下“区间更新时,是往叶子节点(即c[1])拓展” 和 “单点查询时,往树根(即c[n])拓展” 的原因

    举例说明吧:

    c[1]=a[1]; 

    c[2]=a[1]+a[2];

    c[3]=a[3];

    c[4]=a[1]+a[2]+a[3]+a[4];

    c[5]=a[5];

    c[6]=a[5]+a[6];

    c[7]=a[7];

    c[8]=a[1]+...+a[8];

    假如我要更新区间[3,7],那么我首先更新[1,7],即该区间+1;再更新[1,2],该区间-1:

    由于更新时往叶子节点拓展的,所以更新[1,7]时:c[7]++,c[6]++,c[4]++。

    可以发现,这三个正好包含了a[1]~a[7]一次,相当于a[1]~a[7]都更新一遍 而更新[1,2]时:c[2]--,包含了a[1],a[2]。

    那么当我查询某一值a[i],由于是往根节点拓展,所以每个包含a[i]的c[j]都会遇到一次(更新时每个a[i]变化一次)

    即之前凡是更新过的值我都会加上。

    如我想查询a[2],那么a[2]=c[2]+c[4]+c[8]=-1+1+0=0;

    查询a[3],那么a[3]=c[3]+c[4]+c[8]=0+1+0=1。

    1)树状数组区间更新,单点求值方法

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define LL long long
    #define M 1000000000
    #define maxn 22222
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    using namespace std;
    int a[maxn],c[maxn];
    int dp[maxn][12];//dp[i][j]表示以a[i]为结尾长度为j的子序列;dp[i][j]=∑dp[k][j-1]且a[i]>a[k]
    int n,k;
    int lowbit(int x)
    {
        return x&(-x);
    }
    int sum(int i)
    {
        LL res=0;
        while(i<=n)
        {
            res=(res+c[i])%M;
            i+=lowbit(i);
        }
        return res;
    }
    void update(int i,int x)
    {
        while(i)
        {
            c[i]=(c[i]+x)%M;
            i-=lowbit(i);
        }
    }
    int main()
    {
       while(scanf("%d%d",&n,&k)>0)
       {
           memset(dp,0,sizeof(dp));
           for(int i=1;i<=n;i++)
           {
               scanf("%d",&a[i]);
               dp[a[i]][1]=1;
           }
           for(int i=2;i<=k;i++)
           {
               memset(c,0,sizeof(c));
               for(int j=i-1;j<=n;j++)
               {
                   dp[a[j]][i]=sum(a[j]);//求长度为i以a[j]点为结束的逆序对数值
                   update(a[j],dp[a[j]][i-1]);//更新处在0~a[j]的数逆序对+dp[a[j]][i-1]
               }
           }
           LL ans=0;
           for(int i=1;i<=n;i++)
           {
               ans=(ans+dp[a[i]][k])%M;
           }
           printf("%lld
    ",ans);
       }
    }
    View Code

    2)树状数组单点更新,区间求值方法

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define LL long long
    #define M 1000000000
    #define maxn 22222
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    using namespace std;
    int a[maxn],c[maxn];
    int dp[maxn][12];//dp[i][j]表示以a[i]为结尾长度为j的子序列;dp[i][j]=∑dp[k][j-1]且a[i]>a[k]
    int n,k;
    int lowbit(int x)
    {
        return x&(-x);
    }
    void update(int x,int y)
    {
        while(x<=n)
        {
            c[x]=(c[x]+y)%M;
            x+=lowbit(x);
        }
    }
    int sum(int x)
    {
        int res=0;
        while(x)
        {
            res=(res+c[x])%M;
            x-=lowbit(x);
        }
        return res;
    }
    int main()
    {
        while(scanf("%d%d",&n,&k)>0)
        {
            for(int i=1;i<=n;i++)
            {
                scanf("%d",&a[i]);
                dp[i][1]=1;
            }
            int res;
            for(int i=1,j=n;i<j;i++,j--) res=a[i],a[i]=a[j],a[j]=res;
            for(int i=2;i<=k;i++)
            {
                memset(c,0,sizeof(c));
                for(int j=i-1;j<=n;j++)
                {
                    dp[a[j]][i]=sum(a[j]);//求在0~a[j]比a[j]小的所在点逆序对之和
                    update(a[j],dp[a[j]][i-1]);//更新a[j]点的逆序对
                }
            }
            int ans=0;
            for(int i=1;i<=n;i++) ans=(ans+dp[a[i]][k])%M;
            printf("%d
    ",ans);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    KMP算法
    Python 正则表达式
    Python 装饰器
    C/C++ 之输入输出
    PAT(Basic Level)--个位数统计
    Java 接口与抽象类
    Java集合-01概述
    数据结构--红黑树
    数据结构--(AVL)平衡二叉树
    数据结构--二叉搜索树
  • 原文地址:https://www.cnblogs.com/lienus/p/4128772.html
Copyright © 2011-2022 走看看