杭电1166 敌兵布阵 线段树and树状数组

杭电 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 :就是求该点之前的所有值得和

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值