传送门:1166 敌兵布阵
题意:
线段树,单点加或减,求区间和。
貌似应当是 用树状数组来做。
思路:
我的思路是参考了线段树 区间替换,区间求和 的代码。
单点加或减不就是长度为1的区间的替换吗?
但是Wrong Answer了,暂时也不想debug,只想快的过知识点和收集板子。
My Wrong Code And Online AC Code
/*
My Wrong Code
*/
#include <bits/stdc++.h>
using namespace std;
const int maxn=50005;
int sum[maxn<<2],flag[maxn<<2];
int A[maxn];
int index=0;
inline void PushUp(int idx)
{
sum[idx]=sum[idx<<1]+sum[idx<<1|1];
}
void Build(int idx,int left,int right)
{
// flag : -1表示没有懒惰标记
flag[idx]=-1;
if(left==right)
{
sum[idx]=A[index++];
//scanf("%d",sum+idx);
return ;
}
int mid=(left+right)>>1;
Build(idx<<1,left,mid);
Build(idx<<1|1,mid+1,right);
PushUp(idx);
}
// 懒惰标记的‘下放操作’
inline void PushDown(int idx,int left,int mid)
{
int L=idx<<1,R=L+1;
sum[L]=(mid-left+1)*flag[idx];
sum[R]=sum[idx]-sum[L];
flag[L]=flag[R]=flag[idx];
flag[idx]=-1;
}
int L,R,val;
void Update(int idx,int left,int right)
{
if(L<=left && right<=R)
{
sum[idx]=(right-left+1)*val;
// 放置懒惰标记
flag[idx]=val;
return;
}
int mid=(left+right)>>1;
if(flag[idx]!=-1) PushDown(idx,left,mid);
if(L<=mid) Update(idx<<1,left,mid);
if(mid<R) Update(idx<<1|1,mid+1,right);
PushUp(idx);
}
int Query(int idx,int left,int right)
{
if(L<=left && right<=R)
return sum[idx];
int mid=(left+right)>>1,ans=0;
if(flag[idx]!=-1) PushDown(idx,left,mid);
if(L<=mid) ans+=Query(idx<<1,left,mid);
if(mid<R) ans+=Query(idx<<1|1,mid+1,right);
return ans;
}
int main()
{
int t;
scanf("%d",&t);
for(int i=1; i<=t; i++)
{
printf("Case %d:
",i);
//freopen("test.txt","r",stdin);
int N,M;
scanf("%d",&N);
for(int i=0; i<N; i++)
{
scanf("%d",&A[i]);
}
Build(1,1,N);
//scanf("%d",&M);
while(true)
{
string s;
cin>>s;
if(s=="End")
break;
if(s=="Query")
{
scanf("%d%d",&L,&R);
printf("%d
",Query(1,1,N));
}
else if(s=="Add")
{
scanf("%d",&L);
R=L;
scanf("%d",&val);
val=A[L-1]+val;
Update(1,1,N);
}
else if(s=="Sub")
{
scanf("%d",&L);
R=L;
scanf("%d",&val);
val=A[L-1]-val;
Update(1,1,N);
}
}
}
return 0;
}
/*
Online AC Code
单点更新,区间求和
参考:http://chuanwang66.iteye.com/blog/1418459
*/
#include <stdio.h>
#define lson l , m , rt << 1
//预定义lson为: l,m,rt<<1
//——凡是写lson的地方,替换为l,m,rt<<1,表示原节点的左儿子
#define rson m + 1 , r , rt << 1 | 1
const int maxn = 55555;
int sum[maxn<<2];
void PushUP(int rt)
{
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
//sum[rt]=sum[rt/2]+sum[rt/2+1];
}
/*
最外层调用build(1,n,1):
下标1为根的子树表示线段范围是[1,n],且为离散型(整数点)
*/
void build(int l,int r,int rt)
{
//递归结束条件:到达线段树叶子节点(编号rt),则可以输入其值
if (l == r)
{
scanf("%d",&sum[rt]); //之所以将rt作为输入,目的是在构造叶子节点的时候要用
return ;
}
int m = (l + r) >> 1;
build(lson); //build(l,m,rt<<1);
build(rson); //build(m+1,r,rt<<1|1);
PushUP(rt);
}
//线段树(单点/非区间)更新
void update(int p,int add,int l,int r,int rt)
{
if (l == r)
{
sum[rt] += add;
return ;
}
//下面采用分治法(之所以从中间劈开是为了逐渐减小问题规模)
int m = (l + r) >> 1;
if (p <= m) update(p, add, lson); //#define lson l m rt<<1
else update(p, add, rson); //#define rson m+1 r rt<<1|1
//回溯返回时,更新上层节点
PushUP(rt);
}
//线段树查询
/*
query a b对应
query(a,b,1,n,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) >> 1;
int ret = 0;
//下面采用分治法(从中间劈开是为了逐渐减小问题规模)
//注:L<=m表示查询的范围在左儿子中“有”;R>m表示查询的范围在右儿子中“有”!
if (L <= m) ret += query(L, R, lson); //#define lson l m rt<<1
if (R > m) ret += query(L, R, rson); //#define rson m+1 r rt<<1|1
return ret;
}
int main()
{
int T, n; //T组数据,N个营地
scanf("%d",&T);
for (int cas = 1 ; cas <= T ; cas ++)
{
printf("Case %d:
",cas);
scanf("%d",&n);
build(1, n, 1);
//注意:根节点编号为1,然后从左到右、从上到下编号。这样,左儿子编号=rt<<1, 右儿子编号=rt<<1|1;
//build(1,n,1)的结果将是:以标号1(param3)为根的子树的叶节点分别对应sum[1],sum[2],sum[3],...sum[n](n=10);
//且构建的这棵树的每个内部节点i的sum[i]均为它所在子树的所有叶节点∑sum[j](j为叶节点编号)
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
",query(a, b, 1, n, 1)); //查询区间[a,b],总区间[1,n],树根1
else if (op[0] == 'S') update(a, -b, 1, n, 1); //单点a, 增量-b, 总区间[1,n],树根1
else update(a, b, 1, n, 1); //单点a, 增量 b, 总区间[1,n],树根1
}
}
return 0;
}