树状数组两种重要的应用:插点问线(nyoj的士兵杀敌一二)
一.插点问线
http://acm.nyist.net/JudgeOnline/problem.php?pid=116
int lowbit(int n)
{
return n & (-n);
}
void add(int i, int plus)
{
while(i <= N)
{
res[i] += plus;
i += lowbit(i);
//插点问线从当前节点先父节点更新,父节点要更新,字节点是不需要更新的;
}
}
int sum(int n)
{
int result = 0;
while(n > 0)
{
result += res[n];
n -= lowbit(n);
//求和从当前节点向下求和
}
return result;
}
int main()
{
int M, i, key, num, plus, start, end;
char str[10];
scanf("%d%d", &N, &M);
for(i = 1; i <= N; ++i)
{
scanf("%d", &key);
add(i, key);
}
while(M--)
{
scanf("%s", str);
if(strcmp(str, "ADD") == 0)
{
scanf("%d%d", &num, &plus);
add(num, plus);
}
else
{
scanf("%d%d", &start, &end);
printf("%d\n", sum(end) - sum(start - 1));
}
}
return 0;
}
二,插线问点
http://acm.nyist.net/JudgeOnline/problem.php?pid=123
void add(int i, int plus)
{
while(i > 0)
{
dp[i] += plus;
i -= lowbit(i);
//从当前节点向下覆盖
}
}
int sum(int n)
{
int sum = 0;
while(n <= N)
{
sum +=dp[n];
n += lowbit(n);
//从当前节点向上求和
}
return sum;
}
int main()
{
int T, i, j, first, end, score, res;
char str[10];
scanf("%d%d", &T, &N);
memset(dp, 0, sizeof(dp));
while(T--)
{
scanf("%s", str);
if(strcmp(str, "ADD") == 0)
{
scanf("%d%d%d", &first, &end, &score);
add(first - 1, -score); //dp[]中1到first-1 都减score;
add(end, score); //dp[]中1到end都加score;
// 操作的结果就是first 到 end 区间上 值被更新;
}
else
{
scanf("%d", &res);
printf("%d\n", sum(res));
}
}
return 0;
}
另一种方法;
add(first,score);//操作的结果是first到N加上score;
add(end+1,-score);//end+1 到 N 减去 score
//所以操作的结果同样是first 到end 加上了score;
void(int i,int plus)
{
while(i<=N)
{
a[i] += plus;
i += lowbit(i);
}
}
这里的问点的操作,比上面的好理解;//这两种操作的差别的原因是add();的不同;
int sum(int x)
{
int res=0;
while(x>0)
{
res += a[x];
x -= lowbit(x);
}
}