线段树与树状数组草稿

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
questions.

Each question is like this (L,R

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

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

Input

In the first line there is two numbers N 
and
.

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

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

N1000,Q100000,LR,1a[i]31 

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
",dp2[r][r]);
            else printf("%d
",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 ",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
", nMax-nMin);
        }
    }
    return 0;
}
View Code

《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:
", 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
", Query(1, a, b));
        }
    }
    return 0;
}
View Code

《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
", Query(1, a, b));
            }
        }
    }
    return 0;
}
View Code

《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
", Query(1, a, b));
        }
    }
    return 0;
}
View Code

《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
",t);  
            if(t!=-1)  
            Update(1, t, t, k);  
        }
    }
    return 0;
}
View Code

《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
",Min);
    }
    return 0;
}
View Code

线段树求逆序数, 其实就是将输入的数组元素依次插入线段树, 线段树的段值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
",ss.size()-1);
    }
    return 0;
}
View Code

反对暴力, 提倡和谐!

原文地址:https://www.cnblogs.com/acm1314/p/4539463.html