程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> HDU4638[離線樹狀數組]

HDU4638[離線樹狀數組]

編輯:C++入門知識

分析問題並考慮已知的數據結構。

細節處理很重要。

#include <iostream>   
#include <cstdio>   
#include <cstring>   
#include <algorithm>   
#include <vector>   
using namespace std;  
//用標記數組的話果然不對  比如樣例3 1 2 5 4,走一遍之後,再從頭開始進行遍歷的話,3+1,3-1都有標記,後面的都加了1   
//還有得注意到底是什麼情況下用sum(a)-sum(b);什麼情況下只用sum(a)就夠了   
//樹狀數組更新之後 其後所有的數都會更新 切記   
int a[100005];  
int visit[100005];  
int c[100005];  
int ret[100005];  
int num[100005];  
struct vv  
{  
    int l,r;  
    int id;  
}v[100005];  
int max(int a,int b)  
{  
    if(a<b) return b;  
    else return a;  
}  
int min(int a,int b)  
{  
    if(a<b) return a;  
    else return b;  
}  
int cmp(struct vv x,struct vv y)  
{  
    return x.l<y.l;  
}  
int lowbit(int x)  
{  
    return x&-x;  
}  
void update(int a,int b)  
{  
    for(int i=a;i<=100005;i+=lowbit(i))  
        c[i]+=b;  
}  
int sum(int a)  
{  
    int ret=0;  
    for(int i=a;i>=1;i-=lowbit(i))  
        ret+=c[i];  
    return ret;  
}  
int main()  
{  
    int case_num;  
    scanf("%d",&case_num);  
    while(case_num--)  
    {  
        memset(c,0,sizeof(c));  
        memset(num,0,sizeof(num));  
        int n,m;  
        scanf("%d%d",&n,&m);  
        for(int i = 1;i <= n;i++)  
        {  
            scanf("%d",&a[i]);  
            num[a[i]]=i;  
        }  
        num[0] = n+10;  
        num[n+1] = n+10;  
        for(int i = 1;i <= n;i++)  
        {  
            if(i < num[a[i]-1] && i < num[a[i]+1])     //d段數加1   
                update(i,1);  
            else if(i > num[a[i]-1] && i > num[a[i]+1])   //段數減1   
                update(i,-1);  
        }  
        for(int i=0;i<m;i++)  
        {  
            scanf("%d%d",&v[i].l,&v[i].r);  
            v[i].id=i;//利用這個Id進行編號是個好技巧...應該可以這麼輸出?   
        }  
        sort(v,v+m,cmp);  
        int i = 1;  
        int j = 0;  
        while(j < m)       //m次詢問   
        {  
            while(i <= n && i < v[j].l)   //從左到右刪除   
            {  
                if(i > num[a[i]-1] && i > num[a[i]+1])   //刪除a[i],則把原來加的一,減去   
                    update(i,-1);  
                else if(i < num[a[i]-1] && i < num[a[i]+1])  
                {  
                    int Min = min(num[a[i]-1],num[a[i]+1]);  
                    int Max = max(num[a[i]-1],num[a[i]+1]);  
                    update(i,-1);               //願來加的一減去   
                    update(Min,1);              //少了a[i],則相應段數增加   
                    update(Max,1);              // . ...   
                }  
                else if(i < num[a[i]-1])  
                {  
                    update(i,-1);  
                    update(num[a[i]-1],1);  
                }  
                else  
                {  
                    update(i,-1);  
                    update(num[a[i]+1],1);  
                }  
                i++;  
            }  
                ret[v[j].id]= sum(v[j].r); //保存答案   
                j++;  
        }  
        for(int i=0;i<m;i++)  
            printf("%d\n",ret[i]);  
    }  
     return 0;  
}  

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
//用標記數組的話果然不對  比如樣例3 1 2 5 4,走一遍之後,再從頭開始進行遍歷的話,3+1,3-1都有標記,後面的都加了1
//還有得注意到底是什麼情況下用sum(a)-sum(b);什麼情況下只用sum(a)就夠了
//樹狀數組更新之後 其後所有的數都會更新 切記
int a[100005];
int visit[100005];
int c[100005];
int ret[100005];
int num[100005];
struct vv
{
    int l,r;
    int id;
}v[100005];
int max(int a,int b)
{
    if(a<b) return b;
    else return a;
}
int min(int a,int b)
{
    if(a<b) return a;
    else return b;
}
int cmp(struct vv x,struct vv y)
{
    return x.l<y.l;
}
int lowbit(int x)
{
    return x&-x;
}
void update(int a,int b)
{
    for(int i=a;i<=100005;i+=lowbit(i))
        c[i]+=b;
}
int sum(int a)
{
    int ret=0;
    for(int i=a;i>=1;i-=lowbit(i))
        ret+=c[i];
    return ret;
}
int main()
{
    int case_num;
    scanf("%d",&case_num);
    while(case_num--)
    {
        memset(c,0,sizeof(c));
        memset(num,0,sizeof(num));
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i = 1;i <= n;i++)
        {
            scanf("%d",&a[i]);
            num[a[i]]=i;
        }
        num[0] = n+10;
        num[n+1] = n+10;
        for(int i = 1;i <= n;i++)
        {
            if(i < num[a[i]-1] && i < num[a[i]+1])     //d段數加1
                update(i,1);
            else if(i > num[a[i]-1] && i > num[a[i]+1])   //段數減1
                update(i,-1);
        }
        for(int i=0;i<m;i++)
        {
            scanf("%d%d",&v[i].l,&v[i].r);
            v[i].id=i;//利用這個Id進行編號是個好技巧...應該可以這麼輸出?
        }
        sort(v,v+m,cmp);
        int i = 1;
        int j = 0;
        while(j < m)       //m次詢問
        {
            while(i <= n && i < v[j].l)   //從左到右刪除
            {
                if(i > num[a[i]-1] && i > num[a[i]+1])   //刪除a[i],則把原來加的一,減去
                    update(i,-1);
                else if(i < num[a[i]-1] && i < num[a[i]+1])
                {
                    int Min = min(num[a[i]-1],num[a[i]+1]);
                    int Max = max(num[a[i]-1],num[a[i]+1]);
                    update(i,-1);               //願來加的一減去
                    update(Min,1);              //少了a[i],則相應段數增加
                    update(Max,1);              // . ...
                }
                else if(i < num[a[i]-1])
                {
                    update(i,-1);
                    update(num[a[i]-1],1);
                }
                else
                {
                    update(i,-1);
                    update(num[a[i]+1],1);
                }
                i++;
            }
                ret[v[j].id]= sum(v[j].r); //保存答案
                j++;
        }
        for(int i=0;i<m;i++)
            printf("%d\n",ret[i]);
    }
     return 0;
}



 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved