文章出處

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\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;
}
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:\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;
}
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\n", 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\n", 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\n",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\n",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\n",ss.size()-1);
    }
    return 0;
}
View Code

反對暴力, 提倡和諧!

 


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()