注册 登录  
 加关注
查看详情
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

力的博客

小歇一会 heiheidemaolv

 
 
 

日志

 
 

hdu1166 敌兵布阵(线段树)解题报告  

2011-11-20 11:24:23|  分类: ACM/C/C++/OJ |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

http://acm.hdu.edu.cn/showproblem.php?pid=1166

敌兵布阵
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)

Problem Description
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的责骂的.


Input
第一行一个整数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条命令


Output
对第i组数据,首先输出“Case i:”和回车,
对于每个Query询问,输出一个整数并回车,表示询问的段中的总人数,这个数最多不超过1000000。


Sample Input
1
10
1 2 3 4 5 6 7 8 9 10
Query 1 3
Add 3 6
Query 2 7
Sub 10 2
Add 6 3
Query 3 10
End


Sample Output
Case 1:
6
33
59

题意:计算经常变动的数据段的和。

解题代码:

 树状数组的解法:

#include <stdio.h>
#include <string.h>
#define N 50005
#define low(x) x&(-x)
int  n,c[N];
char s[10]; 

void add(int x,int t)
{
     for(int i=x;i<=n;i+=low(i))
         c[i]+=t;
}

int sum(int x)
{
    int res=0;
    for(int i=x;i>0;i-=low(i))
        res+=c[i];
    return res;
}

int main()
{
    int T,i,t,a,b,cnt=1;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(i=0;i<=n;++i)
            c[i]=0;
        for(i=1;i<=n;++i)
        {
            scanf("%d",&t);
            add(i,t);
        }
       
        printf("Case %d:\n",cnt++);
        while(scanf("%s",s)!=EOF)
        {
            if(s[0]=='Q')
            {
               scanf("%d %d",&a,&b);
               t=sum(b)-sum(a-1);
               printf("%d\n",t);
            }
            else if(s[0]=='A')
            {
               scanf("%d %d",&a,&t);
               add(a,t);
            }
            else if(s[0]=='S')
            {
               scanf("%d %d",&a,&t);
               add(a,-t);   
            }
            else break;
        }
    }
    return 0;
}

线段树解法:

#include <stdio.h>
#include <string.h>
#define N 50005

struct Node
{
   int l,r,sum;
}p[3*N];
int que[N];
char s[10];//保留请求字符 add sub query

void build(int k,int s,int t)
{
   p[k].l=s,p[k].r=t;
   if(t-s==1) p[k].sum=que[s];
  
   if(t-s>1)
   {
      int mid=(s+t)>>1,lk=k<<1,rk=lk+1;
     
      build(lk,s,mid);
      build(rk,mid,t);
      p[k].sum=p[lk].sum+p[rk].sum;
   }
}

void insert(int k,int s,int t,int v)
{
   if(s<=p[k].l&&p[k].r<=t) p[k].sum+=v;
   else
   {
      int mid=(p[k].l+p[k].r)>>1,lk=k<<1,rk=lk+1;
     
      if(s<mid) insert(lk,s,t,v);
      if(t>mid) insert(rk,s,t,v);
      p[k].sum=p[lk].sum+p[rk].sum;
   }
}

int query(int k,int s,int t)
{
   if(s<=p[k].l&&p[k].r<=t) return p[k].sum;
   else
   {
      int a=0,b=0;
      int mid=(p[k].l+p[k].r)>>1,lk=k<<1,rk=lk+1;
     
      if(s<mid) a=query(lk,s,t);
      if(t>mid) b=query(rk,s,t);
      return a+b;
   }  
}

int main()
{
    int i,n,a,b,t,T,cnt=1;
    scanf("%d",&T);
    while(T--)
    {
       scanf("%d",&n);
       for(i=1;i<=n;++i) scanf("%d",que+i);
       build(1,1,n+1);//由于这里一个节点为最小单元,n要加1
      
       printf("Case %d:\n",cnt++);
       while(scanf("%s",s)!=EOF&&s[0]!='E')
       {
          if(s[0]=='Q')
          {
             scanf("%d %d",&a,&b);
             t=query(1,a,b+1);
             printf("%d\n",t);
          }
          else
          {
             scanf("%d %d",&a,&t);
             if(s[0]=='S') t=-t;
             insert(1,a,a+1,t);
          }
       }
    }
    return 0;
}

解题过程及想法总结:

首先说明一点,这道题暴力可以过。。。暴力的代码就不在此提供了,直接模拟题目即可。

说说线段树的解法:

要理解low(x) x&(-x)和 mid=(s+t)>>1,lk=k<<1,rk=lk+1;这里跟数据结构有关,现在自己都还没有完全理解过来,先在这里留个buf,理解了来补。
  评论这张
 
阅读(178)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018