acmhdu1166 敌兵布阵阵 超时 求改进 树状数...

树状数组-线段树(17)
Time Limit:
MS (Java/Others)&&&&Memory Limit:
K (Java/Others)
Total Submission(s): 55213&&&&Accepted Submission(s): 23144
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的责骂的.
第一行一个整数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以内。
Sample Input
1 2 3 4 5 6 7 8 9 10
Query 3 10
Sample Output
Windbreaker
再来一发 !刚弄懂了学了线段树~,代码已详细注释便于大家和个人理解。
#include&iostream&
#include&sstream&
#include&algorithm&
#include&cstdio&
#include&string.h&
#include&cctype&
#include&string&
#include&cmath&
#include&vector&
#include&stack&
#include&queue&
#include&map&
#include&set&
const int INF=50006;
int sum[INF&&2];//树的结点数应该为线段长度的四倍合适
inline void PushPlus(int rt)
sum[rt]=sum[rt&&1]+sum[rt&&1|1];//某节点的sum值等于两个儿子值之和
void Build(int l,int r,int rt) //建树的过程rt开始从编号为1的结点开始建
//l,r为建树区间
// 由于对二叉树进行前序遍历,每找到一片叶子赋予兵营值,依次从左到右
scanf(&%d&,&sum[rt]);
int m=(l+r)&&1;
Build(l,m,rt&&1);
Build(m+1,r,rt&&1|1);
PushPlus(rt);// 每次回溯完两个孩子,把孩子值合并给他爹爹
void Update(int p,int add,int l,int r,int rt)
//p 为要更新的节点,add要更新的值,l,r线段树起始,rt根节点开始
sum[rt]+=//一旦找到p结点更新此节点值
int m=(l+r)&&1;
if(p&=m) // 两个条件语句相当于二分查找p结点
Update(p,add,l,m,rt&&1);
Update(p,add,m+1,r,rt&&1|1);
PushPlus(rt);//回溯p结点的各个祖先
int Query(int L,int R,int l,int r,int rt)
//L,R分别为要查询的区间,l,r分别为线段树始末位置,rt还是从根节点开始拆分
if(L&=l&&r&=R) // 如果查询区间某部分完全被某一节点覆盖,直接返回此节点属性值
return sum[rt];
int m=(l+r)&&1;
int ans=0;//这个查询过程是从根节点开始自顶下上找到待查询线段的左边界和右边界,
if(L&=m)//这样“夹在树中间的叶子”不重复不遗漏的覆盖了整个待查询线段
ans+=Query(L,R,l,m,rt&&1);
ans+=Query(L,R,m+1,r,rt&&1|1);
int main()
int T,n,a,b;
scanf(&%d&,&T);
for(int i=1; i&=T; i++)
printf(&Case %d:\n&,i);
scanf(&%d&,&n);
Build(1,n,1);
char op[10];
while(scanf(&%s&,op)&&op[0]!='E')
scanf(&%d%d&,&a,&b);
if(op[0]=='Q')
printf(&%d\n&,Query(a,b,1,n,1));
else if(op[0]=='S')
Update(a,-b,1,n,1);
Update(a,b,1,n,1);
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:98618次
积分:3774
积分:3774
排名:第5983名
原创:284篇
转载:32篇
评论:21条
(8)(1)(13)(3)(8)(21)(24)(20)(42)(90)(85)HDU 1166 敌兵布阵(树状数组) - 凌乱的心 - 博客园
把每一件简单的事情做好,就是不简单;把每一件平凡的事情做好,就是不平凡!相信自己,创造奇迹~~
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的责骂的.
第一行一个整数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以内。
Sample Input
1 2 3 4 5 6 7 8 9 10
Query 3 10
Sample Output
分析:树状数组又称二叉索引树
  树状数组直接应用,题目内容可以用数组来来模拟,意思是对下标为1~N的数组动态进行修改和统计某一特定区间[a,b]的数据总和。
  树状数组之所以会比普通示和表述的动态求和快,是因为其保留了存和(开辟数组保留多项的和)的特性,但是其并不像普通示和数组那样将数组的每一项的意义都看做一样的,树状数组中下标为[2^0],[2^1],[2^2]......的意义和普通数组一致,但其他的数组项则只是表示局部(左邻区域)的和值,例如下标为[3]单单表示第三个数据的值,下标为[6]的表示第五和第六个数据和,我们要明确,树状数组是利于处理动态求和的数据存储结构,意思是说他存储的数据是完整的。(看的不是太明白 QAQ)
  将树状数组看成一个数据结构,其包含了一切线性数组的所有信息
  对于正整数x,我们定义lowbit(x)为x放入二进制表达式中最右边的1所对应的值(而不是这个比特序号)。比如,38 288 的二进制是10000,所以lowbit(38 288)=16(二进制是10000)。在程序实现中,lowbit(x)=x&-x。因为计算机里的整数采用补码表示,因此-x实际上是x按位取反,末尾加1后的结果
  38288 = 10000
  -38288 = 10000
  二者按位去"与"后,前面的部分全部变0,之后lowbit保持不变
代码如下:
1 # include&stdio.h&
2 # include&string.h&
4 int n,r[50001];
6 int sum(int x){
//x左边的和
int sum =0 ;
sum += r[x];
x -= x&-x;
//减去2进制中的最后一个1
15 void update(int p,int m){
while(p&=n){
p += p&-p;
22 int main(){
int cas,T,i;
scanf("%d",&T);
for(cas=1; cas&=T; cas++){
memset(r,0,sizeof(r));
scanf("%d",&n);
for(i=1; i&=n; i++){
int c,y=i;
scanf("%d",&c);
update(i,c);
printf("Case %d:\n",cas);
char dos[10];
//用于收集命令
//动态操作
scanf("%s",dos);
if(dos[0] == 'E')
scanf("%d%d",&a,&b);
if(dos[0] == 'Q')
printf("%d\n",sum(b)-sum(a-1));
//询问a~b的和
else if(dos[0] =='A')
update(a,b);
else if(dos[0]=='S')
update(a,-b);
随笔 - 259线段树&&数组数组(22)
Problem Description
Sample Input
Sample Output
树状数组(求指定区间比指定数小的数的个数)
http://blog.csdn.net/wmn_wmn/article/details/8034181
#include &stdio.h&
#include &string.h&
#include &algorithm&
#include &iostream&
const int maxn=300005;
int C[maxn],ans[maxn];
int lowbit(int x)
return x&(-x);
int sum(int x)
int ret=0;
while(x&0)
ret+=C[x];
x-=lowbit(x);
void add(int x,int d)
while(x&=n)
x+=lowbit(x);
struct node
bool operator & (const node &other) const
return num & other.
struct note
int l,r,id,
bool operator & (const note &other)const
return value &other.
int main()
int T,tt=0;
scanf(&%d&,&T);
while(T--)
scanf(&%d%d&,&n,&m);
for(int i=0;i&n;i++)
scanf(&%d&,&a[i].num);
a[i].id=i+1;
for(int i=0;i&m;i++)
scanf(&%d%d%d&,&b[i].l,&b[i].r,&b[i].value);
b[i].id=i+1;
sort(a,a+n);
sort(b,b+m);
memset(C,0,sizeof(C));
for(int i=0;i&m;i++)
while(k&n&&a[k].num&=b[i].value)
add(a[k].id,1);
ans[b[i].id]=sum(b[i].r)-sum(b[i].l-1);
printf(&Case %d:\n&,++tt);
for(int i=1;i&=m;i++)
printf(&%d\n&,ans[i]);
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:259094次
积分:9641
积分:9641
排名:第1239名
原创:746篇
转载:10篇
评论:51条
(1)(5)(4)(4)(3)(4)(4)(8)(8)(22)(21)(9)(23)(30)(38)(40)(30)(28)(28)(33)(30)(57)(43)(66)(62)(47)(59)(45)(2)&&国之画&&&&&&
&& &&&&&&&&&&&&&&&&&&
鲁ICP备号-4
打开技术之扣,分享程序人生!

参考资料

 

随机推荐