题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1166
思路:
将各个独立区间合并,形成多个次大的区间,在将次大区间合并,最后合并成一个大区间,这样可以减少求区间和时的运算次数,以此来降低复杂度,更新小区间的值时,同时更新它所在大区间的值;
本题是线段树模板题,本应该是线段树做的,但由于不会建树且数据不算大,先用数组模拟了。
#include<bits/stdc++.h>
using namespace std;
int q[20][50050];
int w[1000];
int main()
{
long long a,s,d,f;
scanf("%d",&a);
char z[1000];
for(int k=0;k<a;k++)
{
scanf("%d",&s);
for(int i=0;i<s;i++)
{
scanf("%d",&q[0][i]);
}
q[0][s+1]=0;//防止s是奇数时,q[0][s+1]是上次保存的值;
int num=s;
int flag=1;
while(num>1)
{
for(int i=0;i<num;i+=2)
{
q[flag][i/2]=q[flag-1][i]+q[flag-1][i+1];//两两合并形成次大的区间
}
flag++;//flag代表合并几次
num++;
num/=2;
}
printf("Case %d:\n",k+1);
while(scanf("%s",&z))
{
if(z[0]=='A')
{
scanf("%d%d",&d,&f);
d--;//由于从0开始存值,但题目是1开始存值,所以-1;
int flag1=0;
while(flag1<flag)//先更新小区间,再循环更新合并后的大区间
{
q[flag1][d]+=f;
d/=2;
flag1++;
}
}
if(z[0]=='S')
{
scanf("%d%d",&d,&f);
d--;//由于从0开始存值,但题目是1开始存值,所以-1;
int flag1=0;
while(flag1<flag)//先更新小区间,再循环更新合并后的大区间
{
q[flag1][d]-=f;
d/=2;
flag1++;
}
}
if(z[0]=='E')
{
break;
}
if(z[0]=='Q')
{
scanf("%d%d",&d,&f);
d--;//由于从0开始存值,但题目是1开始存值,所以-1;
f--;//由于从0开始存值,但题目是1开始存值,所以-1;
int sum=0;
int flag1=0;
while(d<f)
{
if(d%2)//如果最左边的单位无法进入下一循环 ,便将其独立开来
{
sum+=q[flag1][d];
d++;
}
if(f%2==0)//同理,最右边的单位无法进入下一循环 ,也将其独立开来
{
sum+=q[flag1][f];
f--;
}
if(d==f)//如果左右单位都被独立,剩下一个单位,那么也无法进入下一循环
sum+=q[flag1][d];
d/=2;
f/=2;
flag1++;
if(d==f)//如果进入下一循环 ,只剩一个单位便无法继续循环
sum+=q[flag1][d];
}
if(sum==0&&d==f)//如果一开始d和f就是相同的,那么他们无法进入while,所以sum=0,进行特判
{
sum+=q[flag1][d];
}
printf("%d\n",sum);
}
}
}
return 0;
}
数据小可以数组模拟,但数据一大就凉凉,还是早点学会线段树。