线段树-题解
题目链接 http://acm.nyist.net/JudgeOnline/problem.php?pid=1068
A 操作某一个区间一个数整体加上一个数;
S 操作查询某一个区间的总和,
Q 操作,查询这个区间有多少个奇数.
下面是 线段树延迟更新,奇数的个数更新时注意
如果变化的是奇数,那么 : 现在区间奇数个数=区间长度-原本区间的奇数个数.
下面 延迟更新 模板题目.如果对模板不太理解,请看这个博 或者看代码注释
延迟更新大致思想: 每一次数据更新, [a,b] 区间,则只需要更新到他们的子区间且 满足各个子区间无交集 且 并集为区间 [a,b];每次更新,都更新到这些子区间停止,递归归的时候再更新上面的值. 查询的时候mark,标记的是上次更新到这里停止了,如果要往下走,标记下沉,并计算左右儿子.
#include<cstdio> #include<cstring> #include<cctype> #include<cmath> #include<set> #include<map> #include<list> #include<queue> #include<deque> #include<stack> #include<string> #include<vector> #include<iostream> #include<algorithm> #include<stdlib.h> #include<time.h> using namespace std; typedef long long LL; const int INF=2e9+1e8; const int MOD=1e9+7; const int MAXSIZE=1e6+5; const double eps=0.0000000001; void fre() { freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); } #define memst(a,b) memset(a,b,sizeof(a)) #define fr(i,a,n) for(int i=a;i<n;i++) struct Node { LL odd,sum,mark; LL l,r; } Tree[10005*4]; LL arr[MAXSIZE]; void build(LL l,LL r,LL pos) //建树过程,递归建树 { Tree[pos].mark=0;//初始化标记变量 Tree[pos].l=l,Tree[pos].r=r; LL mid=(l+r)>>1ll; if(l==r) { Tree[pos].sum=arr[l]; if(arr[l]&1ll) Tree[pos].odd=1ll; else Tree[pos].odd=0; return ; } build(l,mid,pos<<1ll); build(mid+1ll,r,pos<<1ll|1ll); //上面是递的过程,下面是归并且求和; Tree[pos].sum=Tree[pos<<1ll].sum+Tree[pos<<1ll|1ll].sum; Tree[pos].odd=Tree[pos<<1ll].odd+Tree[pos<<1ll|1ll].odd; return ; } /** pushdown 函数 将本节点的左右孩子节点的标记变量加上自己节点的标记值, 且把计算结果(通常是求和)并把自己的标记值清空 */ void pushdown(LL pos) { if(Tree[pos].mark) { Tree[pos<<1ll].mark+=Tree[pos].mark,Tree[pos<<1ll|1ll].mark+=Tree[pos].mark; Tree[pos<<1ll].sum+=(Tree[pos<<1ll].r-Tree[pos<<1ll].l+1ll)*Tree[pos].mark; Tree[pos<<1ll|1ll].sum+=(Tree[pos<<1ll|1ll].r-Tree[pos<<1ll|1ll].l+1ll)*Tree[pos].mark; if(Tree[pos].mark&1ll) { Tree[pos<<1ll].odd=(Tree[pos<<1ll].r-Tree[pos<<1ll].l+1ll)-Tree[pos<<1ll].odd; Tree[pos<<1ll|1ll].odd=(Tree[pos<<1ll|1ll].r-Tree[pos<<1ll|1ll].l+1ll)-Tree[pos<<1ll|1ll].odd; } Tree[pos].mark=0; } } /** update 函数 插线函数,递归开始; 如果找到子区间则,更新当前节点,因为只不需要更新到叶子节点, 所以,计算当前值,把当前节点的标记变量 加上 k,停止递归; 否则接着递归下去,每次需要下沉标记,并计算; --> 递归 合并左右孩子节点的值, */ void update(LL l,LL r,LL pos,LL k) { LL mid=(Tree[pos].l+Tree[pos].r)>>1ll; if(Tree[pos].l==l&&Tree[pos].r==r) //如果更新到子区间 { Tree[pos].mark+=k;//标记变量自加 //每次更新都活到这里,所以计算的时候是 k ,不是 mark Tree[pos].sum+=(Tree[pos].r-Tree[pos].l+1ll)*k; // if(k&1ll) Tree[pos].odd=(Tree[pos].r-Tree[pos].l+1ll)-Tree[pos].odd; return ; } pushdown(pos); if(mid<l) update(l,r,pos<<1ll|1ll,k); else if(r<=mid)update(l,r,pos<<1ll,k); else { update(l,mid,pos<<1ll,k); update(mid+1ll,r,pos<<1ll|1ll,k); } Tree[pos].sum=Tree[pos<<1ll].sum+Tree[pos<<1ll|1ll].sum; Tree[pos].odd=Tree[pos<<1ll].odd+Tree[pos<<1ll|1ll].odd; } void Debug(LL pos) { printf("l=%d r=%d sum=%d odd=%d mark=%d\n",Tree[pos].l,Tree[pos].r,Tree[pos].sum,Tree[pos].odd,Tree[pos].mark); if(Tree[pos].l==Tree[pos].r) return ; Debug(pos<<1ll); Debug(pos<<1ll|1ll); } /** query 函数-返回答案 只要查询,并且标记下沉,计算; */ LL query(LL l,LL r,LL pos,char opt) { LL mid=(Tree[pos].l+Tree[pos].r)>>1ll; if(Tree[pos].l==l&&Tree[pos].r==r) { if(opt=='S') return Tree[pos].sum; else return Tree[pos].odd; } pushdown(pos); if(mid<l) return query(l,r,pos<<1ll|1ll,opt); else if(r<=mid) return query(l,r,pos<<1ll,opt); else return query(l,mid,pos<<1ll,opt)+query(mid+1ll,r,pos<<1ll|1ll,opt); } int main() { LL n,m; while(scanf("%lld%lld",&n,&m)+1ll) { LL a,b,c; for(LL i=1ll; i<=n; i++) scanf("%lld",&arr[i]); build(1ll,n,1ll); for(LL i=0; i<m; i++) { char opt[3]; scanf("%s",opt); if(opt[0]=='A') { scanf("%lld%lld%lld",&a,&b,&c); update(a,b,1ll,c); // Debug(1ll); } else { scanf("%lld%lld",&a,&b); printf("%lld\n",query(a,b,1ll,opt[0])); } } } return 0; } /**************************************************/ /** Copyright Notice **/ /** writer: wurong **/ /** school: nyist **/ /** blog : http://blog.csdn.net/wr_technology **/ /**************************************************/