题意:给定一环路中两两相邻结点之间的距离,随机访问任意两个结点之间的距离。
题解:因为访问的次数过多(10000),并且访问的本质就是区间和,所以考虑用树状数组。原数组中每个下标 i 表示他与前一个结点的距离(i 和 i-1),下标为1表示 的是 1 和N 的距离(1和N之间有一条通路)。所以当我们用树状数组(treearr)重构上述数组之后,就可以通过treearr[i] - treearr[j]得到两点间的一种距离,因为是环路,所以还有另一种走法,另一种走法长度为环路总长减去第一种,这样就可以达到O(nlogn)的时间复杂度了。最后比较两种走法长度,取较小的那个。
代码:
#include<iostream>
#include<stdio.h>
//#define scanf scanf_s
using namespace std;
const int maxn = 1e5 + 7;
int treearr[maxn] = {0};
int answers[10004];
int anssize = 0;
int N;
int lowbit(int x)
{
return x & -x;
}
void addval(int pos,int v)
{
for (; pos <= N; pos += lowbit(pos))
{
treearr[pos] += v;
}
}
int ask_interval(int pos)
{
int sum = 0;
for (; pos > 0; pos -= lowbit(pos))
{
sum += treearr[pos];
}
return sum;
}
int main()
{
scanf("%d", &N);
int tmp;
for (int i = 0; i < N-1; i++) //1到N-1
{
scanf("%d", &tmp);
addval(i + 2, tmp);
}
scanf("%d", &tmp); //N与1的距离
addval(1, tmp);
int distN = ask_interval(N);
int M,city1,city2,dist1,dist2;
scanf("%d", &M);
for (int i = 0; i < M; i++)
{
scanf("%d %d", &city1,&city2);
if (city1 > city2)
swap(city1, city2);
int a = ask_interval(city1);
int b = ask_interval(city2);
dist1 = b - a;
dist2 = distN - b + a;
answers[anssize++] = dist1 < dist2 ? dist1 : dist2;
}
for (int i = 0; i < anssize; i++)
{
printf("%d\n", answers[i]);
}
return 0;
}