关闭

线段树(堆式)[单点更新, 区间查询]

569人阅读 评论(0) 收藏 举报


http://acm.hdu.edu.cn/showproblem.php?pid=1166

first blood of seg-tree  单点增减 区间求和

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn=60000+123;
int T[maxn*2];
int M;

int bit(int x)/// get highest 1 in bit-number
{
    if(x==0)return 0;
    int n=1;
    if((x>>16)==0){n+=16; x<<=16;}
    if((x>>24)==0){n+=8; x<<=8;}
    if((x>>28)==0){n+=4; x<<=4;}
    if((x>>30)==0){n+=2; x<<=2;}
    return 32-n-(x>>31);
}

void Updata(int x, int v)
{
    for (T[x+=M]+=v, x>>=1; x; x>>=1) //
        T[x]=T[x<<1]+T[(x<<1)+1]; /// updata father by their children
}

int request(int s, int t)
{
    int res=0;
    for (s+=M-1, t+=M+1; s^t^1; s>>=1, t>>=1)/// change [,] to (,); s^t==1 refer to the interval is empty
    {
        if(~s&1) res+=T[s^1];
        if( t&1) res+=T[t^1];
    }
    return res;
}

int main ()
{
    int cas;
    scanf("%d", &cas);
    for (int I=1; I<=cas; ++I)
    {
        printf("Case %d:\n", I);
        int n;
        ///initialization
        scanf("%d", &n);
        M=1<<(bit(n));
//        printf("---%d  %d\n", M, bit(n));
        memset (T, 0, sizeof(T));
        for (int i=0; i<n; ++i)
            scanf("%d",  &T[i+M]);
        for (int i=(n+M-1)/2; i>0; --i)/// (n+m-1)/2 is largest non-leaf node
            T[i]=T[2*i]+T[2*i+1];

        char op[20];
        int a, b;
        while (~scanf("%s", op))
        {
            if (op[0]=='E')
                break;
            scanf("%d %d", &a, &b);
            if (op[0]=='Q')
                printf("%d\n", request(a-1, b-1));
            if (op[0]=='A')
                Updata(a-1, b);
            if (op[0]=='S')
                Updata(a-1, -b);
        }
    }
    return 0;
}


hdu1754 I Hate It

水题。。。单点替换 区间最值

hdu1394 Minimum Inversion Number

最小逆序数 水题, 直接暴力。 

不过线段树确实优化不少,对求3元组 , 类似ai>aj>ak (i>j>k)这样的也可以用线段树, 下面有例子

单点增减 区间求和

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn=5000+123;
int T[maxn*3];
int a[maxn];
int M;
int bit(int x)/// get highest 1 in bit-number
{
    if(x==0)return 0;
    int n=31;
    if((x>>16)==0){n-=16; x<<=16;}
    if((x>>24)==0){n-=8; x<<=8;}
    if((x>>28)==0){n-=4; x<<=4;}
    if((x>>30)==0){n-=2; x<<=2;}
    return n-(x>>31);
}

int Query(int s, int t)
{
    int res=0;
    for (s+=M-1, t+=M+1; s^t^1; s>>=1, t>>=1)
    {
        if(~s&1) res+=T[s^1];
        if( t&1) res+=T[t^1];
    }
    return res;
}

void Updata(int x)
{
    for (T[x+=M]=1, x>>=1; x; x>>=1)
        T[x]=T[x<<1]+T[(x<<1)+1];
}

void init(int x)
{
    memset (T, 0, sizeof(T));
    M=1<<bit(x);
}

int main ()
{
    int n;
    while (~scanf("%d", &n))
    {
        init(n);
        int inv=0;
        for (int i=0; i<n; ++i)
        {
            scanf("%d", a+i);
            inv+=Query(a[i]+1, n-1);
            Updata(a[i]);
        }
        int ans=inv;
        for (int i=0; i<n-1; ++i)
        {
            inv+=n-a[i]-a[i]-1;
            ans=min(inv, ans);
        }
        printf("%d\n", ans);
    }
    return 0;
}


hdu2795 Billboard

维护最大值 查询时自上向下, 改了N次, 终于过了, 线段树灵活运用才是王道!

单点增减, 区间查询满足给点值的最小下标位置

void Updata (int x, int v)
{
    for (T[x+=M]-=v, x>>=1; x; x>>=1)
        T[x]=max(T[x<<1], T[x<<1|1]);
}

int Query (int s, int t, int x)
{
    int res=-1, r;
    s+=M-1;t+=M+1;
    for (r=1; (r<<1)<t;)
    {
        T[r<<1]>=x?(r<<=1):(r=(r<<1|1));
    }
    if(r>s && r<t)res=r;
    else res=t;
    return res;
}


http://www.acdream.net/problem.php?id=1123

群赛的题, 求一个序列中满足aj>ai>ak 且满足j>i>k的3元组的个数, 和hdu1394 Minimum Inversion Number 类似, 2个线段树维护,

 第一次找aj>ai ,且j>i的对数, 然后再次维护一个新树, 找3元组的个数


类似题解:

dp[i][1] 表示 有多少对 aj > ai,j < i,dp[i][2]表示有多少对ak > aj > ai , k < j < i

dp[i][1] += dp[j][0] ( aj > ai 且 j < i)

dp[i][2] += dp[j][1] ( aj > ai 且 j < i)


long long Request(int s, int t)
{
    long long res=0;
    for (s+=M-1, t+=M+1; s^t^1; s>>=1, t>>=1)
    {
        if(~s&1) res+=T[s^1];
        if( t&1) res+=T[t^1];
    }
    return res;
}

void Updata(int x, int v)
{
    for (T[x+=M]+=v, x>>=1; x; x>>=1)
        T[x]=T[x<<1]+T[x<<1|1];
}

void init()
{
    M=1<<bit(n);
    memset (T, 0, sizeof(T));
}

int main ()
{
    while (~scanf("%d", &n))
    {
        init();
        memset (inv, 0, sizeof(inv));
        for (int i=0; i<n; ++i)/// 得到一次逆序,
        {
            scanf("%d", a+i);
            inv[i]=Request(a[i]+1, n)+inv[i];
            Updata(a[i], 1);
        }
        memset (T, 0, sizeof(T));
        long long ans=0;
        for (int i=0; i<n; ++i)
        {
            ans=Request(a[i]+1, n)+ans;
            Updata(a[i], inv[i]);
        }
        printf("%lld\n", ans);
    }
    return 0;
}



0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:168107次
    • 积分:3511
    • 等级:
    • 排名:第9375名
    • 原创:174篇
    • 转载:10篇
    • 译文:0篇
    • 评论:62条
    最新评论
    ACMer's blog
    http://hi.csdn.net/space-4864697.html