看了网上好多好多好多的代码,能告诉我为啥都用 '>>'这些符号写的么...是感觉很好看么...(确实挺漂亮问题是本来就很蒙的好吧!)还有啊,确实是我实力太差define 定义左右儿子那每次看到函数里就蒙了,我都替换成正常的了
不过幸好在头脑清醒的时候弄明白了一些,虽然现在查询那还是不太懂
好啦好啦不多说了
首先我先把正常有加减乘除符号的代码给贴出来,当初我就是卡在这了,几乎没见到正常的代码= =本来就蒙蒙的
有这里卡住的看到就懂了就不用往下看了,用hdu1166举例子
//hdu 1166 敌兵布阵
#include
#include
int sum[55555*4];
void pushup(int rt)
{
sum[rt] = sum[rt*2]+sum[rt*2+1];
}
void build(int l, int r, int rt)
{
if(l == r)
{
scanf("%d", &sum[rt]);
return ;
}
int m = (l+r)/2;
build(l, m, rt*2);
build(m+1, r, rt*2+1);
pushup(rt);
}
void update(int p, int add, int l, int r, int rt)
{
if(l == r)
{
sum[rt]+=add;
return;
}
int m = (l+r)/2;
if(p <= m)update(p, add, l, m, rt*2);
else update(p, add, m+1, r, rt*2+1);
pushup(rt);
}
int query(int L, int R, int l, int r, int rt)
{
if(L <= l && r <= R)return sum[rt];
int m = (l+r)/2;
int ret = 0;
if(L <= m) ret += query(L, R, l, m, rt*2);
if(R > m)ret += query(L, R, m+1, r, rt*2+1);
return ret;
}
int main()
{
int tt, n, i, j, k, x, y;
scanf("%d", &tt);
for(int cas = 1; cas <= tt; ++cas)
{
printf("Case %d:\n", cas);
scanf("%d", &n);
build(1, n, 1);
char ch[10];
while(scanf("%s", ch),ch[0]-'E')
{
scanf("%d%d", &x, &y);
if(ch[0]=='A')update(x, y, 1, n, 1);
else if(ch[0]=='S')update(x, -y, 1, n, 1);
else printf("%d\n",query(x, y, 1, n, 1));
}
}
return 0;
}
接下来就是我个人看的理解了,有讲错了欢迎指出
1、首先既然是线段树 既然叫做‘树’ 就说明是树状的对吧,这样我们 怎么用这样的树 来记录各种值呢
答案就是 用树叶来记录每个兵营的人数(我们用这道题举例子, 大家快去读一读是中文题欸)
然后每个节点 就能记录下面叶子的和,这样一层一层的记录就好查了
可是大家还是很蒙,怎么记录,怎么查找,你光这么说我也不知道啊,思想谁都会啊!
别急,才开始,且听我用我的方式慢慢道来
2、开始建立树,函数 void build(int l, int r, int rt) 我们都需要准备什么呢? 我这里不用结构体,就用最直白的
准备: rt, 这个是记录当前结点标号的,就是每个节点的下标(index)
l, r, 这个是 当前第 rt 节点 所记录的 左边 和 右边, 就是l到r之间的和
在函数最外面还需要一个sum[ ]数组,用来记录每个节点记录的值
接下来是一个关键点:当l==r的时候代表什么呢?
对!当l==r 说明我们当前第rt个点只有记录了它自己,因为它下面没有了分支所以他是叶子!!
所以build函数一开始就是这样
void build(int l, int r, int rt)
{
if(l == r)//当前发现是叶子,也就是每个兵营
{
scanf("%d", &sum[rt]);//那么我就输入一个要记录的兵营人数
return;//当前找到最底部的叶子了,可以返回了
}
函数还没写完
}
为什么叶子还用sum记录人数呢,你想想l==r是不是说明也是个区间和?只不过这个区间只有一个值,所以没必要开其他数组混淆视听啦
那么每次我怎么找到l == r啊?你去看主函数 ,是不是写作build(1, n, 1); n是我们兵营数量
也就是说我开始时候l从1开始,r从n开始,1到n就是 rt == 1 记录的区间
然后函数发现它不是叶子,就继续执行
void build(int l, int r, int rt)
{
if(l == r)
{
scanf("%d", &sum[rt]);
return;
}
int m = (l+r)/2; //把区间对半分
build(l, m, rt*2);//从l -> m 左半递归
build(m+1, r, rt*2+1);//从 m+1 -> r 右半递归
pushup(rt);
}
pushup用来每次递归已经确立的左子树和右子树的和
void pushup(int rt)
{
sum[rt] = sum[rt*2]+sum[rt*2+1];
}
到这里建树就基本完成了,我实力太差只能写成这样了,我的想法就是理解如何写出的代码,然后用理解的方式背下来,
至少能记住很久,至于真正理解这样的思想就靠题和时间 还有 之后的返工了,我这里只是讲了怎么理解写出来的
看看有木有人看,有木有人理解,要是需要我再写添加add和查询query,其实这俩和建树异曲同工啦大家一起努力啦啦啦
最后提示一下如果用cin和cout一定会超时的,我试着用
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0);
剩下的主函数接着写
}
还需要拆开输出,很费劲,就算我不用endl也才省了20多ms(之前看贴吧有人说endl费时),所以老老实实用scanf和printf吧!