杭电 1166 敌兵布阵
这个题目可以用 线段树 也可以 树状数组
首先用线段树解决:
#include<stdio.h>
#define lson l, m, rt*2 //rt=rt*2 //<<>> 是右移 lson==l,m,rt;
#define rson m+1, r, rt*2+1 //rson== m+l,r,rt m 中点 a+b/2
const int maxn=50005;
int sum[maxn*3];//只要是 *2 以上 就可以了
void build(int l,int r,int rt)
{
if(l==r)
{
scanf("%d",&sum[rt]);//左右点相等 无论线段多长 最后每个点都要有这一步
return;
}
int m=(l+r)/2;//求中点
build(lson);//建立线段树左树
build(rson);//建立线段树右树
sum[rt]=sum[rt*2]+sum[rt*2+1]; //sum[父]==sum[左儿子]+sum[右儿子]
}
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,lson);
else //同理 在右边 就只更新右边的
update(p,add,rson);
sum[rt]=sum[rt*2]+sum[rt*2+1];
}
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,lson);
if(R>m)
ret+=query(L,R,rson);
return ret;
}
int main()
{
int T,n,cas;
scanf("%d",&T);
for(cas=1;cas<=t;cas++)
{
printf("Case %d:\n",cas);//case
scanf("%d",&n);
build(1,n,1);
char op[10];
while(scanf("%s",op))
{
if(op[0]=='E')
break;
int a,b;
scanf("%d%d",&a,&b);
if(op[0]=='Q')
printf("%d\n",query(a,b,1,n,1));
else if(op[0]=='S')
update(a,-b,1,n,1);
else
update(a,b,1,n,1);
}
}
return 0;
}
首先 建树:(线段树左端点,线段树右端点,根节点)
建高为 1 到 n 的树。在建树是 以 (1+n)/2 为顶点。分别建立 左树 右树。逐渐缩小树,当左右儿子一样时,输入该点的值,线段树就建立完全了。 最后当然少不了父节点值=左儿子点值+右儿子点值。
当然建树也可以建成一个初值为0的树,然后输入的值依次通过update输入树中。
然后 更新线段树的值:(更改值的位置,增加或者变为的值,线段树左端点,线段树右端点,根节点)
通过和建树差不多的方法找到该点,增加 则在原值上加减 ,变值 则直接等于就可以了。
同样少不了 父节点值=左儿子点值+右儿子点值。
最后 求和:(求和区间左值,求和区间右值,线段树左值,线段树右值,根节点)
根据输入的区间,在线段树里面找里面还暂时完整的小树,,将他们只和加上就是最后要求的值。
下面用 树状数组和离散化下面用 树状数组和离散化#include<stdio.h>
#include<string.h>
int n,a[50005];
int lowbit(int x)
{
return x&(-x);
}
void Sub(int i,int delta)
{
int j;
for(j=i;j<=n;j+=lowbit(j))
a[j]-=delta;
}
void Add(int i, int delta)
{
int j;
for(j=i;j<=n;j+=lowbit(j))
a[j]+=delta;
}
int sum(int k)
{
int i,ans = 0;
for(i=k;i>0;i-=lowbit(i))
ans+=a[i];
return ans;
}
void Query(int x, int y)
{
printf("%d\n",sum(y)-sum(x-1));
}
int main()
{
int i,t,T,x,y,d;
scanf("%d",&T);
t=0;
char op[10];
while(T--)
{
memset(a,0,sizeof(a));
t++;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d",&d);
Add(i,d);
}
printf("Case %d:\n",t);
while(scanf("%s",op))
{
if(op[0]=='E')
break;
scanf("%d%d",&x,&y);
if(op[0]=='Q')
Query(x,y);
if(op[0]=='A')
Add(x,y);
if(op[0]=='S')
Sub(x,y);
}
}
return 0;
}
lowbit :用于找出每一个点的管辖区域。
sub 和 add :就相当于其他题目中的 update。将该点值在原值基础上加减 不能直接变换值得大小。同时 将管辖这一点的根节点同步更新。
sum :就是求该点之前的所有值得和。