1. 程式人生 > >敵兵布陣(HDU-1166)

敵兵布陣(HDU-1166)

由於 線段 turn 存在 正整數 acm 敵人 build con

題目描述:

C國的死對頭A國這段時間正在進行軍事演習,所以C國間諜頭子Derek和他手下Tidy又開始忙乎了。A國在海岸線沿直線布置了N個工兵營地,Derek和Tidy的任務就是要監視這些工兵營地的活動情況。由於采取了某種先進的監測手段,所以每個工兵營地的人數C國都掌握的一清二楚,每個工兵營地的人數都有可能發生變動,可能增加或減少若幹人手,但這些都逃不過C國的監視。
中央情報局要研究敵人究竟演習什麽戰術,所以Tidy要隨時向Derek匯報某一段連續的工兵營地一共有多少人,例如Derek問:“Tidy,馬上匯報第3個營地到第10個營地共有多少人!”Tidy就要馬上開始計算這一段的總人數並匯報。但敵兵營地的人數經常變動,而Derek每次詢問的段都不一樣,所以Tidy不得不每次都一個一個營地的去數,很快就精疲力盡了,Derek對Tidy的計算速度越來越不滿:"你個死肥仔,算得這麽慢,我炒你魷魚!”Tidy想:“你自己來算算看,這可真是一項累人的工作!我恨不得你炒我魷魚呢!”無奈之下,Tidy只好打電話向計算機專家Windbreaker求救,Windbreaker說:“死肥仔,叫你平時做多點acm題和看多點算法書,現在嘗到苦果了吧!”Tidy說:"我知錯了。。。"但Windbreaker已經掛掉電話了。Tidy很苦惱,這麽算他真的會崩潰的,聰明的讀者,你能寫個程序幫他完成這項工作嗎?不過如果你的程序效率不夠高的話,Tidy還是會受到Derek的責罵的.

輸入

第一行一個整數T,表示有T組數據。
每組數據第一行一個正整數N(N<=50000),表示敵人有N個工兵營地,接下來有N個正整數,第i個正整數ai代表第i個工兵營地裏開始時有ai個人(1<=ai<=50)。
接下來每行有一條命令,命令有4種形式:

(1) Add i j,i和j為正整數,表示第i個營地增加j個人(j不超過30)
(2)Sub i j ,i和j為正整數,表示第i個營地減少j個人(j不超過30);
(3)Query i j ,i和j為正整數,i<=j,表示詢問第i到第j個營地的總人數;
(4)End 表示結束,這條命令在每組數據最後出現;
每組數據最多有40000條命令

輸出

對第i組數據,首先輸出“Case i:”和回車,
對於每個Query詢問,輸出一個整數並回車,表示詢問的段中的總人數,這個數保持在int以內。

思路:

最近剛學的三個最新的數據結構都可以寫這題,可以說這題很經典吧!不管對於那種數據結構都很典型,也能體現每種數據結構的特點,下面一一解釋:

分塊:

由於每次詢問時都要查詢一個區間的和,若每次都掃描一遍,肯定會超時,那麽換一種想法,若將整個區間劃分為若幹個小塊,存下他們的和,當訪問一個區間時,其中包含的所有完整的小塊便可直接O(1)查詢和,至於兩邊多余的部分便可以暴力求和了,至於更新,則只需額外再修改一下該點所在的塊的和即可。

樹狀數組:

樹狀數組我覺得是三個數據結構中最難理解的,咳咳。。比如有一個長度為6的數組,二進制表示為110,此時可以將其拆分為110=100+10,進行處理,至於如何拆分,用lowbit即可,這樣只需預處理出所有這樣形式的和即可,這樣做大大減少了復雜度,至於如何實現,可見下方代碼。

線段樹:

這種方法就比較套路了,將(1-n)的數組,劃分為一棵二叉樹,分割為一個個小區間,分別存在每個子樹(或葉子)中,最後查詢時用樹形遍歷即可,這樣的復雜度也會大大減少。線段樹的大致結構為,build()函數建樹,update()函數更新結構,最後query()函數查詢信息,三個函數的寫法都十分相似,但要學會區分。

//分塊做法
#include<cstdio>
const int maxn=50005,S=100;
int a[maxn],sum[maxn];
int query(int l,int r){
    int pl=l/S,pr=r/S,ans=0;
    if(pl==pr){
        for(int i=l;i<=r;i++)
            ans+=a[i];
    }
    else {
        for(int i=l;i<(pl+1)*S;i++)ans+=a[i];
        for(int i=pr*S;i<=r;i++)ans+=a[i];
        for(int i=pl+1;i<pr;i++)ans+=sum[i];
    }
    return ans;
}
int main(){
    int T,kase=0;
    scanf("%d",&T);
    while(T--){
        int n,tot=0;
        scanf("%d",&n);
        for(int i=0;i<n;i++)a[i]=sum[i]=0;
        for(int i=0;i<n;i++){
            scanf("%d",&a[i]);
            tot+=a[i];
            if(i%S==S-1)sum[i/S]=tot,tot=0;
        }
        printf("Case %d:\n",++kase);
        char cmd[10];
        while(scanf("%s",cmd)==1){
            if(cmd[0]=='E')break;
            int x,y;
            scanf("%d%d",&x,&y);
            x--,y--;
            if(cmd[0]=='Q'){
                printf("%d\n",query(x,y));
            }
            else if(cmd[0]=='A'){
                y++;
                int px=x/S;
                a[x]+=y;
                sum[px]+=y;
            }
            else if(cmd[0]=='S'){
                y++;
                int px=x/S;
                a[x]-=y;
                sum[px]-=y;
            }
        }
    }
    return 0;
}

//樹狀數組寫法
#include<cstdio>
#include<cstring>
const int maxn=50005;
int a[maxn],c[maxn],n;
int lowbit(int x){
    return x&(-x);
}
//以下兩個函數很重要,要完全理解。
int sum(int x){
    int ans=0;
    while(x>0){
        ans+=c[x];
        x-=lowbit(x);
    }
    return ans;
}
void update(int x,int y){
    while(x<=n){
        c[x]+=y;
        x+=lowbit(x);
    }
}
int main(){
    int T,kase=0;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        memset(c,0,sizeof(c));
        for(int i=1;i<=n;i++)scanf("%d",&a[i]),update(i,a[i]);
        char cmd[10];
        printf("Case %d:\n",++kase);
        while(scanf("%s",cmd)==1){
            if(cmd[0]=='E')break;
            int x,y;
            scanf("%d%d",&x,&y);
            if(cmd[0]=='A'){
                update(x,y);
                a[x]+=y;
            }
            else if(cmd[0]=='S'){
                update(x,-y);
                a[x]+=y;
            }
            else if(cmd[0]=='Q')
                printf("%d\n",sum(y)-sum(x-1));
        }
    }
    return 0;
}

//線段樹寫法
#include<cstdio>
#define M 100005
#define ll long long
int a[M];
struct tree{
    int l,r,sum;
}tree[M*4];
void Up(int p){
    tree[p].sum=tree[p<<1].sum+tree[p<<1|1].sum;
}
//以下三個函數十分重要,要完全理解
void build(int l,int r,int p){
    tree[p].l=l,tree[p].r=r;
    if(l==r){
        tree[p].sum=a[l];
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,2*p);
    build(mid+1,r,2*p+1);
    Up(p);
}
void add(int x,int a,int p){
    if(tree[p].l==tree[p].r){
        tree[p].sum+=a;
        return;
    }
    int mid=(tree[p].l+tree[p].r)>>1;
    if(x<=mid)add(x,a,p<<1);
    else add(x,a,p<<1|1);
    Up(p);
}
int query(int l,int r,int p){
    //printf("%d %d %d\n",l,r,p);
    if(tree[p].l==l&&tree[p].r==r){
        return tree[p].sum;
    }
    int mid=(tree[p].l+tree[p].r)>>1;
    if(r<=mid)return query(l,r,p<<1);
    else if(l>mid)return query(l,r,p<<1|1);
    else {
        return query(l,mid,p<<1)+query(mid+1,r,p<<1|1);
    }
}
int main(){
    int T,kase=0;
    scanf("%d",&T);
    while(T--){
        printf("Case %d:\n",++kase);
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        build(1,n,1);
        char cmd[5];
        while(scanf("%s",cmd)==1){
            if(cmd[0]=='E')break;
            int x,y;
            scanf("%d%d",&x,&y);
            if(cmd[0]=='A'){
                add(x,y,1);
            }
            else if(cmd[0]=='S'){
                add(x,-y,1);
            }
            else if(cmd[0]=='Q'){
                printf("%d\n",query(x,y,1));
            }
        }
    }
    return 0;
}

敵兵布陣(HDU-1166)