线段树与树状数组草稿

http://bestcoder.hdu.edu.cn/contests/contest_showproblem.php?cid=604&pid=1002

Dylans loves sequence

Accepts: 249

Submissions: 806

Time Limit: 2000/1000 MS (Java/Others)

Memory Limit: 131072/131072 K (Java/Others)

Problem Description

Dylans is given N 
numbers a[1]....a[N]

And there are Q 
questions.

Each question is like this (L,R)

his goal is to find the “inversions” from number L 
to number R 
.

more formally,his needs to find the numbers of pair(x,y 
), that L≤x,y≤R 
and x<y 
and a[x]>a[y]

Input

In the first line there is two numbers N 
and Q 
.

Then in the second line there are N 
numbers:a[1]..a[N]

In the next Q 
lines,there are two numbers L,R 
in each line.

N≤1000,Q≤100000,L≤R,1≤a[i]≤2 31 −1

Output

For each query,print the numbers of "inversions”

Sample Input

3 2
3 2 1
1 2
1 3

Sample Output

1
3

Hint

You shouldn‘t print any space in each end of the line in the hack data.

动态规划写法:

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <string>
#include <vector>
#include <cmath>
using namespace std;
int dp[1005][1005],dp2[1005][1005];
int main()
{
    int n,Q;
    while(scanf("%d%d",&n,&Q)==2)
    {
        int a[1005];
        for(int i=0;i<n;++i) scanf("%d",&a[i]);
        for(int i=0;i<n;++i)
        {
            dp[i][i]=0;
            for(int j=i+1;j<n;++j)
            {
                dp[i][j]=dp[i][j-1];
                if(a[j]<a[i]) dp[i][j]++;
            }
        }
        for(int i=0;i<n;++i)
        {
            dp2[i][0]=dp[0][i];
            for(int j=1;j<=i;++j) dp2[i][j]=dp2[i][j-1]+dp[j][i];
        }
        while(Q--)
        {
            int l,r;
            scanf("%d%d",&l,&r);
            l--;r--;
            if(l==0) printf("%d\n",dp2[r][r]);
            else printf("%d\n",dp2[r][r]-dp2[r][l-1]);
        }
    }
    return 0;
}
//树状数组写法#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int i,j,m,n,p,k,a[1005],q,b[1005],tree[1005],l,r,ans[1005][1005];
int lowbit(int x) { return x&-x; }
int ask(int x) {
     int sum=0;
     for (;x;x-=lowbit(x)) sum+=tree[x];
     return sum;
}
int ins(int x)
{
      for (;x<=b[0];x+=lowbit(x)) tree[x]++;
}
int main()
{
     scanf("%d%d",&n,&q);
     for (i=1;i<=n;++i) scanf("%d",&a[i]),b[i]=a[i];
     sort(b+1,b+n+1);
     b[0]=unique(b+1,b+n+1)-(b+1);
     for (i=1;i<=n;++i) a[i]=lower_bound(b+1,b+b[0]+1,a[i])-b;
     for (i=1;i<=n;++i)
     {
          memset(tree,0,sizeof(tree));
          for (j=i;j<=n;++j)
          {
               ans[i][j]=ans[i][j-1]+ask(b[0])-ask(a[j]);
               ins(a[j]);
          }
     }
     for (;q--;)
    {
         scanf("%d%d",&l,&r);
         printf("%d\n",ans[l][r]);
    }
}

线段树:

《1》http://poj.org/problem?id=3264(简单题,练模板)

#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;

const int MAXN = 200005;
const int INF = 1000000000;
int nMax, nMin;

struct Node{
    int L, R;
    int nMin, nMax;
}segTree[MAXN*3];

int a[MAXN];

void Build(int i, int L, int R)
{
    segTree[i].L = L;
    segTree[i].R = R;
    if(L==R)
    {
        segTree[i].nMin = segTree[i].nMax = a[L];
        return;
    }
    int mid = (L+R)>>1;
    Build(i<<1, L, mid);
    Build(i<<1|1, mid+1, R);
    segTree[i].nMin=min(segTree[i<<1].nMin, segTree[i<<1|1].nMin);
    segTree[i].nMax=max(segTree[i<<1].nMax, segTree[i<<1|1].nMax);
}
void Query(int i, int L, int R)
{
    if(segTree[i].nMax<=nMax&&segTree[i].nMin>=nMin) return;
    if(segTree[i].L==L&&segTree[i].R==R)
    {
        nMax = max(segTree[i].nMax, nMax);
        nMin = min(segTree[i].nMin, nMin);
        return;
    }
    int mid = (segTree[i].L+segTree[i].R)>>1;
    if(R<=mid) Query(i<<1, L, R);
    else if(L>mid) Query(i<<1|1, L, R);
    else
    {
        Query(i<<1, L, mid);
        Query(i<<1|1, mid+1, R);
    }
}

int main()
{
    int n, q, L, R, i;
    while(scanf("%d%d", &n, &q)!=EOF)
    {
        for(i=1; i<=n; i++)
        scanf("%d", &a[i]);
        Build(1, 1, n);
        for(i=1; i<=q; i++)
        {
            scanf("%d%d", &L, &R);
            nMax = -INF; nMin = INF;
            Query(1, L, R);
            printf("%d\n", nMax-nMin);
        }
    }
    return 0;
}

《2》http://acm.hdu.edu.cn/showproblem.php?pid=1166(简单题, 用模板)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int MAXN = 50000+5;
int num[MAXN];

struct Node{
    int l, r;
    int nSum;
}segTree[MAXN*3];

void Build(int i, int l, int r)
{
    segTree[i].l = l;
    segTree[i].r = r;
    if(l==r)
    {
        segTree[i].nSum=num[l];
        return;
    }
    int mid=(l+r)>>1;
    Build(i<<1, l, mid);
    Build(i<<1|1, mid+1, r);
    segTree[i].nSum=segTree[i<<1].nSum+segTree[i<<1|1].nSum;
}

void Add(int i, int t, int b)
{
    segTree[i].nSum+=b;
    if(segTree[i].l==t&&segTree[i].r==t) return;
    int mid=(segTree[i].l+segTree[i].r)>>1;
    if(t<=mid) Add(i<<1, t, b);
    else Add(i<<1|1, t, b);
}

int Query(int i, int l, int r)
{
    if(segTree[i].l==l&&segTree[i].r==r)
    return segTree[i].nSum;
    int mid=(segTree[i].l+segTree[i].r)>>1;
    if(r<=mid) return Query(i<<1, l, r);
    else if(l>mid) return Query(i<<1|1, l, r);
    else return Query(i<<1, l, mid)+Query(i<<1|1, mid+1, r);
}

int main()
{
    int T;
    int kase;
    int n, i;
    char str[10];
    int a, b;
    scanf("%d", &T);
    for(kase=1; kase<=T; kase++)
    {
        scanf("%d", &n);
        for(i=1; i<=n; i++)
        scanf("%d", &num[i]);
        Build(1, 1, n);
        printf("Case %d:\n", kase);
        while(scanf("%s", &str))
        {
            if(str[0]==‘E‘) break;
            //if(strcmp(str,"End")==0) break;
            scanf("%d%d", &a, &b);
            if(str[0]==‘A‘) Add(1, a, b);
            //if(strcmp(str, "Add")==0) Add(1, a, b);
            else if(str[0]==‘S‘) Add(1, a, -b);
            //else if(strcmp(str, "Sub")==0) Add(1, a, -b);
            else printf("%d\n", Query(1, a, b));
        }
    }
    return 0;
}

《3》http://poj.org/problem?id=3468(进阶题)

#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;

const int MAXN = 100000;
int num[MAXN];
struct Node{
    int l, r;
    long long nSum;
    long long Inc;
}segTree[MAXN*3];

void Build(int i, int l, int r)//建立线段树
{
    segTree[i].l=l;
    segTree[i].r=r;
    segTree[i].Inc=0;
    if(l==r)
    {
        segTree[i].nSum=num[l];
        return;
    }
    int mid=(l+r)>>1;
    Build(i<<1, l, mid);
    Build(i<<1|1, mid+1, r);
    segTree[i].nSum=segTree[i<<1].nSum+segTree[i<<1|1].nSum;
}
void Add(int i, int a, int b, long long c)//在结点i的区间(a,b)上增加c
{
    if(segTree[i].l==a&&segTree[i].r==b)//这个过程和建树过程相似。
    {
        segTree[i].Inc+=c;
        return;
    }
    segTree[i].nSum+=c*(b-a+1);
    int mid=(segTree[i].l+segTree[i].r)>>1;
    if(b<=mid) Add(i<<1, a, b, c);
    else if(a>mid) Add(i<<1|1, a, b, c);
    else
    {
        Add(i<<1, a, mid, c);
        Add(i<<1|1, mid+1, b, c);
    }
}

long long Query(int i, int a, int b)//查询a-b的总和
{
    if(segTree[i].l==a&&segTree[i].r==b)
    {
        return segTree[i].nSum+(b-a+1)*segTree[i].Inc;
    }
    segTree[i].nSum+=(segTree[i].r-segTree[i].l+1)*segTree[i].Inc;
    int mid=(segTree[i].l+segTree[i].r)>>1;
    Add(i<<1, segTree[i].l, mid, segTree[i].Inc);
    Add(i<<1|1, mid+1, segTree[i].r, segTree[i].Inc);
    segTree[i].Inc = 0;
    if(b<=mid) return Query(i<<1, a, b);
    else if(a>mid) return Query(i<<1|1, a, b);
    else return Query(i<<1, a, mid)+Query(i<<1|1, mid+1, b);
}
int main()
{
    int n, q;
    int i;
    int a, b, c;
    char ch;
    while(scanf("%d%d", &n, &q)!=EOF)
    {
        for(i=1; i<=n; i++)
        scanf("%d", &num[i]);
        Build(1, 1, n);
        for(i=1; i<=q; i++)
        {
            cin>>ch;
            if(ch==‘C‘)
            {
                scanf("%d%d%d", &a, &b, &c);
                Add(1, a, b, c);
            }
            else
            {
                scanf("%d%d", &a, &b);
                printf("%I64d\n", Query(1, a, b));
            }
        }
    }
    return 0;
}

《4》http://acm.hdu.edu.cn/showproblem.php?pid=1754(简单题)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int MAXN = 200000+10;
int num[MAXN];

struct Node
{
    int l, r;
    int nMax;
}segTree[MAXN*4];

void Build(int i, int l, int r)
{
    segTree[i].l=l;
    segTree[i].r=r;
    if(l==r)
    {
        segTree[i].nMax=num[l];
        return;
    }
    int mid=(segTree[i].l+segTree[i].r)>>1;
    Build(i<<1, l, mid);
    Build(i<<1|1, mid+1, r);
    segTree[i].nMax=max(segTree[i<<1].nMax, segTree[i<<1|1].nMax);
}

void Update(int i, int l, int r, int x)
{
    if(segTree[i].l==l&&segTree[i].r==r)
    {
        segTree[i].nMax=x;
        return;
    }
    int mid=(segTree[i].l+segTree[i].r)>>1;
    if(r<=mid)
        Update(i<<1, l, r, x);
    else if(l>mid)
        Update(i<<1|1, l, r, x);
    else
    {
        Update(i<<1, l, mid, x);
        Update(i<<1|1, mid+1, r, x);
    }
    segTree[i].nMax=max(segTree[i<<1].nMax, segTree[i<<1|1].nMax);
}

int Query(int i, int l, int r)
{
    if(segTree[i].l==l&&segTree[i].r==r)
    {
        return segTree[i].nMax;
    }
    int mid=(segTree[i].l+segTree[i].r)>>1;
    if(r<=mid)
        return Query(i<<1, l, r);
    else if(l>mid)
        return Query(i<<1|1, l, r);
    else
        return max(Query(i<<1, l, mid), Query(i<<1|1, mid+1, r));
}

int main()
{
    int n, m;
    while(scanf("%d%d", &n, &m)!=EOF)
    {
        int i, j, k, a, b;
        char s[2];
        for(i=1; i<=n; i++)
            scanf("%d", &num[i]);
        Build(1, 1, n);
        for(i=0; i<m; i++)
        {
            scanf("%s%d%d", s, &a, &b);
            if(s[0]==‘U‘)
                Update(1, a, a, b);
            else
                printf("%d\n", Query(1, a, b));
        }
    }
    return 0;
}

《5》http://acm.hdu.edu.cn/showproblem.php?pid=2795

线段树, 适用的范围还真广!!。 这个题看到别人能用线段树切, 也是非常的机智。

/*以墙的高度和广告数量的较小值建线段树 ,
树中v表示该段中最大空余量,idid表示最大
空余量所在行 (从上开始记)。每次贴广告
先比较空余量, 从上到下,空余足够就贴,
再根据查找到的空余行进行点更新。。。
*/
#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;

const int MAXN = 200000+10;
int h, v, n;

struct Node
{
    int l, r;
    int id;
    int v;
}segTree[MAXN*4];

void Build(int i, int l, int r)
{
    segTree[i].l = l;
    segTree[i].r = r;
    segTree[i].id = l;
    segTree[i].v = v;
    if(l==r)
        return;
    int mid=(l+r)>>1;
    Build(i<<1, l, mid);
    Build(i<<1|1, mid+1, r);
}

int Query(int i, int val)
{
    if(segTree[i].v<val)
        return -1;
    if(segTree[i].l==segTree[i].r)
        return segTree[i].id;
    if(segTree[i<<1].v>=val)
        return Query(i<<1, val);
    else
        return Query(i<<1|1, val);
}

void Update(int i, int l, int r, int val)
{
    if(segTree[i].l==l&&segTree[i].r==r)
    {
        segTree[i].v-=val;
        return;
    }
    int mid=(segTree[i].l+segTree[i].r)>>1;
    if(r<=mid)
        Update(i<<1, l, r, val);
    else if(l>mid)
        Update(i<<1|1, l, r, val);
    if(segTree[i<<1].v>segTree[i<<1|1].v)
    {
        segTree[i].v = segTree[i<<1].v;
        segTree[i].id = segTree[i<<1].id;
    }
    else
    {
        segTree[i].v = segTree[i<<1|1].v;
        segTree[i].id = segTree[i<<1|1].id;
    }
}

int main()
{
    while(scanf("%d%d%d", &h, &v, &n)!=EOF)
    {
        int j, k;
        if(h>n)
            h = n;
        Build(1, 1, h);
        for(int q=0; q<n; q++)
        {
            scanf("%d", &k);
            int t;
            t = Query(1, k);
            printf("%d\n",t);
            if(t!=-1)
            Update(1, t, t, k);
        }
    }
    return 0;
}

《6》http://acm.hdu.edu.cn/showproblem.php?pid=1394(求逆序数)

线段树法:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

const int MAXN=5000+10;
int a[MAXN];

struct Node
{
    int l,r;
    int sum;
}segTree[MAXN*3];

void Build(int i,int l,int r)
{
    segTree[i].l=l;
    segTree[i].r=r;
    if(l==r)
    {
        segTree[i].sum=0;
        return;
    }
    int mid=(l+r)>>1;
    Build(i<<1,l,mid);
    Build((i<<1)|1,mid+1,r);
    segTree[i].sum=0;
}

void add(int i,int t,int val)
{
    segTree[i].sum+=val;
    if(segTree[i].l==segTree[i].r)
    {
        return;
    }
    int mid=(segTree[i].l+segTree[i].r)>>1;
    if(t<=mid) add(i<<1,t,val);
    else add((i<<1)|1,t,val);
}

int sum(int i,int l,int r)
{
    if(segTree[i].l==l&&segTree[i].r==r)
      return segTree[i].sum;
    int mid=(segTree[i].l+segTree[i].r)>>1;
    if(r<=mid) return sum(i<<1,l,r);
    else if(l>mid)  return sum((i<<1)|1,l,r);
    else return sum(i<<1,l,mid)+sum((i<<1)|1,mid+1,r);
}

int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        Build(1,0,n-1);
        for(int i=0;i<n;i++)
          scanf("%d",&a[i]);
        int ans=0;
        for(int i=0;i<n;i++)
        {
            ans+=sum(1,a[i],n-1);
            add(1,a[i],1);
        }
        int Min=ans;
        for(int i=0;i<n;i++)
        {
            ans-=a[i];//减少的逆序数
            ans+=n-a[i]-1;//增加的逆序数
            if(ans<Min)Min=ans;
        }
        printf("%d\n",Min);
    }
    return 0;
}

线段树求逆序数, 其实就是将输入的数组元素依次插入线段树, 线段树的段值sum表示该段中以插入的数字个数。每次插入,计算已插入的比插入数大的数的个数,总和就是逆序数。
树状数组法:(先占个坑, 嘿嘿!)

《7》http://poj.org/problem?id=2528(线段树+离散)

这道题看似并不复杂,然而此坑甚深。 本来想暴力一下的, 结果,,,,, 其实大多数的能用线段树解决的问题,都有比较简单的暴力思路, 然而大都卡时间。这就体现出线段树的高效性啦! 。 这也是为何算法令人如痴如醉的一个原因-------------高效性。

暴力该打:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<set>
using namespace std;

const int MAXN = 10000000+10;
int a[MAXN];

int main()
{
    int T, n;
    int l, r;
    scanf("%d", &T);
    while(T--)
    {
        set<int> ss;
        memset(a, 0, sizeof(a));
        scanf("%d", &n);
        for(int i=1; i<=n; i++)
        {
            scanf("%d%d", &l, &r);
            for(int j=l; j<=r; j++)
            a[j] = i;
        }
        for(int i=0; i<MAXN; i++)
        ss.insert(a[i]);
        printf("%d\n",ss.size()-1);
    }
    return 0;
}

反对暴力, 提倡和谐!

时间: 2024-11-05 14:29:31

线段树与树状数组草稿的相关文章

Vijos P1066 弱弱的战壕【多解,线段树,暴力,树状数组】

弱弱的战壕 描述 永恒和mx正在玩一个即时战略游戏,名字嘛~~~~~~恕本人记性不好,忘了-_-b. mx在他的基地附近建立了n个战壕,每个战壕都是一个独立的作战单位,射程可以达到无限(“mx不赢定了?!?”永恒[email protected][email protected]). 但是,战壕有一个弱点,就是只能攻击它的左下方,说白了就是横纵坐标都不大于它的点(mx:“我的战壕为什么这么菜”ToT).这样,永恒就可以从别的地方进攻摧毁战壕,从而消灭mx的部队. 战壕都有一个保护范围,同它的攻击

Curious Robin Hood(树状数组+线段树)

1112 - Curious Robin Hood    PDF (English) Statistics Forum Time Limit: 1 second(s) Memory Limit: 64 MB Robin Hood likes to loot rich people since he helps the poor people with this money. Instead of keeping all the money together he does another tri

浅谈二维中的树状数组与线段树

一般来说,树状数组可以实现的东西线段树均可胜任,实际应用中也是如此.但是在二维中,线段树的操作变得太过复杂,更新子矩阵时第一维的lazy标记更是麻烦到不行. 但是树状数组在某些询问中又无法胜任,如最值等不符合区间减法的询问.此时就需要根据线段树与树状数组的优缺点来选择了. 做一下基本操作的对比,如下图. 因为线段树为自上向下更新,从而可以使用lazy标记使得矩阵的更新变的高校起来,几个不足就是代码长,代码长和代码长. 对于将将矩阵内元素变为某个值,因为树状数组自下向上更新,且要满足区间加法等限制

[luogu P3801] 红色的幻想乡 [线段树][树状数组]

题目背景 蕾米莉亚的红雾异变失败后,很不甘心. 题目描述 经过上次失败后,蕾米莉亚决定再次发动红雾异变,但为了防止被灵梦退治,她决定将红雾以奇怪的阵势释放. 我们将幻想乡看做是一个n*m的方格地区,一开始没有任何一个地区被红雾遮盖.蕾米莉亚每次站在某一个地区上,向东南西北四个方向各发出一条无限长的红雾,可以影响到整行/整列,但不会影响到她所站的那个地区.如果两阵红雾碰撞,则会因为密度过大而沉降消失.灵梦察觉到了这次异变,决定去解决它.但在解决之前,灵梦想要了解一片范围红雾的密度.可以简述为两种操

【算法系列学习】线段树vs树状数组 单点修改,区间查询 [kuangbin带你飞]专题七 线段树 A - 敌兵布阵

https://vjudge.net/contest/66989#problem/A 单点修改,区间查询 方法一:线段树 http://www.cnblogs.com/kuangbin/archive/2011/08/15/2139834.html 1 #include<iostream> 2 #include<cstdio> 3 #include<string> 4 #include<cstring> 5 #include<cmath> 6 #

hdu1540 Tunnel Warfare 线段树/树状数组

During the War of Resistance Against Japan, tunnel warfare was carried out extensively in the vast areas of north China Plain. Generally speaking, villages connected by tunnels lay in a line. Except the two at the ends, every village was directly con

UVA 11990 ”Dynamic“ Inversion(线段树+树状数组)

[题目链接] UVA11990 [题目大意] 给出一个数列,每次删去一个数,求一个数删去之前整个数列的逆序对数. [题解] 一开始可以用树状数组统计出现的逆序对数量 对于每个删去的数,我们可以用线段树求出它在原序列中的逆序对贡献 在线段树的每个区间有序化数据,就可以二分查找出这个数在每个区间的位置, 这样就处理出了划分出的区间的贡献,先用答案减去这一部分 接下来考虑已经删去部分的容斥,我们发现只要对删去部分再做一次类似的操作, 将这一部分跟当前删去数求一次贡献就是刚才多减去的部分,将这部分的答案

士兵杀敌(四)(树状数组+线段树)

士兵杀敌(四) 时间限制:2000 ms  |  内存限制:65535 KB 难度:5 描述 南将军麾下有百万精兵,现已知共有M个士兵,编号为1~M,每次有任务的时候,总会有一批编号连在一起人请战(编号相近的人经常在一块,相互之间比较熟悉),最终他们获得的军功,也将会平分到每个人身上,这样,有时候,计算他们中的哪一个人到底有多少军功就是一个比较困难的事情,军师小工的任务就是在南将军询问他某个人的军功的时候,快速的报出此人的军功,请你编写一个程序来帮助小工吧. 假设起始时所有人的军功都是0. 输入

Color the ball(树状数组+线段树)

Color the ball Time Limit : 9000/3000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other) Total Submission(s) : 3   Accepted Submission(s) : 1 Problem Description N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <= b),lele便为骑上他的“小飞鸽"牌电动车从气球a开始到气球b