樹狀數組兩種重要的應用:插點問線(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);
}
}