敵兵布陣(HDU-1166)
題目描述:
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)