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的责骂的.
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
Windbreaker
这个题第一次做单纯的用了状态模拟,然后,不用想,TLE,然后才知道要用线段树,然而一开始并不知道线段树是什么鬼,花了一下午照着
某位大神的代码敲了一边又看了许久,终于有点头绪啦,至少知道线段树是什么玩意了。。。先传最low版本的代码,更高级的等待我去探索
(代码中加了带有自己的理解的注释,希望那些不明白的同鞋更好的理解)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 50005
using namespace std;
struct seg
{
int l;
int r;
int n;
}Tree[4 * N];//这里必须注意要开4倍,虽然理论上只要2倍,而且有时3倍也会过(和测试数据有关系),但是开4倍是最保险的,可以网上搜一下这个问题,有专门讨论的
void build(int l,int r,int k)//线段树构造函数
{
int mid;
Tree[k].l = l;
Tree[k].r = r;
Tree[k].n = 0;
if(l == r)//构造结束条件左区间等于右区间,即已经到达叶节点
{
return ;
}
mid = (l + r ) / 2;
build(l,mid,2 * k);//递归建立k节点的左子树
build(mid + 1,r,2 * k + 1);//建立右子树
}
void add(int n,int d,int k)
{
int mid;
if(Tree[k].l == Tree[k].r && Tree[k].l == d)//循环结束条件,同时将标号为d的叶子节点值加n,开始回溯
{
Tree[k].n += n;
return ;
}
mid = (Tree[k].l + Tree[k].r) >> 1;
if(d <= mid)
{
add(n,d,2 * k);
}
else
{
add(n,d,2 * k + 1);
}
Tree[k].n = Tree[2 * k].n + Tree[2 * k + 1].n;//回溯过程中改变各个相连区间的值:节点值等于左右两个孩子的值之和
}
int ans;
void query(int l,int r,int k)
{
int mid;
if(Tree[k].l == l && Tree[k].r == r)//递归结束条件:找到了区间
{
ans+=Tree[k].n;
return;
}
mid = (Tree[k].l + Tree[k].r) >> 1;
if(r <= mid)//如果需要找的区间在k节点的左子树上
{
query(l,r,2 * k);
}
else if(l > mid)//如果区间在k节点的右子树上
{
query(l,r,2 * k + 1);
}
else//如果需要找的区间不再同一棵子树上,用mid值将需要寻找的区间分为两部分,这样就变成了寻找两个分别在同一棵子树上的区间
{
query(l,mid,2 * k);//k的左子树
query(mid + 1,r,2 * k + 1);//k的右子树
}
}
int main()
{
int Case,TT;
int n;
int i;
int temp;
char str[11];
int a,b;
scanf("%d",&TT);
for(Case = 1; Case <= TT; Case++)
{
scanf("%d",&n);
build(1,n,1);
for(i = 1; i <= n; i++)
{
scanf("%d",&temp);
add(temp,i,1);//初始化存入各个元节点的值,将temp值存入标号为i的叶子节点
}
printf("Case %d:\n",Case);
while(scanf("%s",str),strcmp(str,"End"))
{
scanf("%d%d",&a,&b);
if(strcmp(str,"Add") == 0)
{
add(b,a,1);//往标号为a的叶节点中加入b值,第三个参数为1是为了保证每个区间在叶节点更新值后通过回溯也更新区间值
}
else if(strcmp(str,"Sub") == 0)
{
add(-b,a,1);//相当于减去b
}
else
{
ans = 0;
query(a,b,1);
printf("%d\n",ans);
}
}
}
return 0;
}
第二种方法琢磨透了。。带注释贴上。。(补充于20150803 14:00)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define N 50005
using namespace std;
int Tree[N << 2];
void PushUp(int rt) //用于更新父节点的值
{
Tree[rt] = Tree[rt << 1] + Tree[rt << 1 | 1];
}
void build(int l,int r,int rt)//建立线段树
{
int mid;
if(l == r )
{
scanf("%d",&Tree[rt]);//递归找到叶节点后直接将数据读入叶节点
//printf("%d",Tree[rt]);
return;
}
mid = (l + r) >> 1;
build(l,mid,rt << 1);
build(mid + 1,r,rt << 1 | 1);//注意这里的|运算符是因为进行了左移运算才能等价于+1运算,因为左移后最右端对数字由0补齐,| 1后变成了1,所以等价于加1
PushUp(rt);
}
void Update(int p,int add,int l,int r,int rt)//更新区间,p为需要改变的叶子节点编号,add为改变的值,l,r为控制递归的参数
{
int mid;
if(l == r)
{
Tree[rt] += add;
return;
}
mid = (l + r ) >> 1;//等价于除2
if(p <= mid)
{ Update(p,add,l,mid,rt << 1);}
else
{ Update(p,add,mid + 1,r,rt << 1 | 1);}
PushUp(rt);
}
int ans;
int query(int ll,int rr,int l,int r,int rt)//ll为需要查询区间的左端,rr为右端,l,r为当前节点的区间,用于控制递归层数,rt为当前节点
{
int mid,ret = 0;
if(ll <= l && rr >= r)//递归结束条件:找到一个当前区间包括需要查询的区间的节点
{
return Tree[rt];
}
mid = (l + r) >> 1;
if(ll <= mid) {ret += query(ll,rr,l,mid,rt << 1); }//只要需要查询的左区间小于当前区间的中值,那么当前区间的左子树中一定包含有要查询的区间,直接递归调用函数即可,
if(rr > mid) {ret += query(ll,rr,mid + 1,r,rt << 1 | 1);}//同上,这两步可能会使需要查询的区间分为几部分
return ret;
}
int main()
{
int T;
int n;
int a,b;
int Case;
char str[11];
//freopen("FileIn.txt","r",stdin);
//freopen("FileOut.txt","w",stdout);
scanf("%d",&T);
for(Case = 1;Case <= T;Case++)
{
printf("Case %d:\n",Case);
scanf("%d",&n);
build(1,n,1);
while(scanf("%s",str) != EOF)
{
if(str[0] == 'E') { break;}
scanf("%d%d",&a,&b);
if(str[0] == 'A')
{
Update(a,b,1,n,1);
}
else if(str[0] == 'S')
{
Update(a,-b,1,n,1);
}
else if(str[0] == 'Q')
{
ans = query(a,b,1,n,1);
printf("%d\n",ans);
}
}
}
//fclose(stdin);
//fclose(stdout);
return 0;
}