pku 3468 线段树-又是成段修改

虽然又是成段修改,但与 hdu 1698 有点不同

真的是越写越觉得线段树高深,灵活

说说这题,一开始感觉与hdu 1698 差不多(我的感觉向来单纯),很快就写了出来,结果

是果断的WA了,这里的传递的细节还是很值得注意的

WA了,然后想在结构体中增加一个flag域来记录这个区间是否全部的增加都相同,相同则向子

结点传递,在插入时并没有计算出区间的sum ,而是在查询时计算,不过这样让我WA了,TLE了

无数次,原因是,这样在查询是很不好处理

最后接近崩溃,还是乖乖地在insert中计算好sum,总算是过了

#include<stdio.h> #define N 100005 struct node{ int l; int r; __int64 sum; __int64 add; // bool flag; }; node tree[3*N]; int arr[N]; void buid_tree(int k,int left,int right) { int mid; int L,R; tree[k].l = left; tree[k].r = right;/*printf("((%d) %d %d) ",k,tree[k].l,tree[k].r);*/ tree[k].add = 0;// tree[k].flag = true; if(tree[k].r-tree[k].l == 1){ tree[k].sum = arr[tree[k].l]; return ; } L = 2*k; R = L+1; mid = (tree[k].l+tree[k].r)/2; buid_tree(L,left,mid); buid_tree(R,mid,right); tree[k].sum = tree[L].sum+tree[R].sum; return ; } void tree_insert(int k,int left,int right,int data) { int mid; int L,R; if(left <= tree[k].l && right >= tree[k].r){ //tree[k].flag = true; tree[k].add += data;//printf("((%d) %d %d %d) ",tree[k].add,k,tree[k].l,tree[k].r); tree[k].sum += (tree[k].r-tree[k].l)*data; return ; } L = 2*k; R = L+1; if(tree[k].add){ tree[L].add += tree[k].add; tree[R].add += tree[k].add; tree[L].sum += (tree[L].r-tree[L].l)*tree[k].add; tree[R].sum += (tree[R].r-tree[R].l)*tree[k].add; tree[k].add = 0; //tree[L].flag = tree[R].flag = true; // tree[k].flag = false; } mid = (tree[k].l+tree[k].r)/2; if(left < mid) tree_insert(L,left,right,data); if(right > mid) tree_insert(R,left,right,data); tree[k].sum = tree[L].sum+tree[R].sum; return ; } __int64 tree_search(int k,int left,int right) { int mid; int L,R; __int64 ls = 0,rs = 0; mid = (tree[k].l+tree[k].r)/2; L = 2*k; R = L+1; if(left <= tree[k].l && right >= tree[k].r){//printf("(%d)-",tree[k].flag); return tree[k].sum; //return tree_search(L,left,mid)+tree_search(R,mid,right); } if(tree[k].add){ tree[L].add += tree[k].add; tree[R].add += tree[k].add; tree[L].sum += (tree[L].r-tree[L].l)*tree[k].add; tree[R].sum += (tree[R].r-tree[R].l)*tree[k].add; tree[k].add = 0; } if(left < mid) ls = tree_search(L,left,right); if(right > mid) rs = tree_search(R,left,right); return ls+rs; } int main() { int n,q; int a,b,c; int i; char ch; while(scanf("%d %d",&n,&q) == 2){ for(i = 1;i <= n;i++){ scanf("%d",&arr[i]); } buid_tree(1,1,n+1); while(q--){ getchar(); scanf("%c ",&ch); if(ch == 'Q'){ scanf("%d %d",&a,&b); printf("%I64d/n",tree_search(1,a,b+1)); } if(ch == 'C'){ scanf("%d %d %d",&a,&b,&c); tree_insert(1,a,b+1,c); } } } return 0; }

 

过是过了,去看了一下status大受打击…………我的用时为1873ms别人竟可以做到250ms,也不知道是怎么写出来的

 

下面是转的:虽不是传说中的250ms 但还是要膜拜下

http://hi.baidu.com/%C1%D9%CA%B1%B1%B8%D3%C3%D5%CB%BA%C5/blog/item/c3fc48277975543fd507420b.html

而张神牛给出的前缀和与差分的做法令人咋舌。。。。

可知,前缀和与差分是相对于数组的逆运算。

所以差分的前缀和就是数组,

而差分在原数组进行区间修改时只需要修改边际的两个节点即可,非常快,

所以就成了维护差分的前缀和的前缀和,

即是数组的前缀和,

但是维护差分的前缀和的前缀和则较为简单。

记数组{ai}前缀和的前缀和位SSi,

前缀和为Si,

再记数组{i*ai}的前缀和为Ti,

则可知SSi=(i+1)*Si-Ti(推一下就知道。。。)

所以只需要再维护一个{i*ai}的前缀和即可。

这样写的话,区间维护区间求和就完全变成了单元素修改前缀求和,用树状数组就能解决。。。。。。

但是每次查询要取4个前缀和。

 

刚开始我还只是以为这个方法只是写起来方便,效率未必高,

结果两个方法都试过交上去以后发现,

无论是时间空间还是编程复杂度都直接秒杀传统方法。。。。。

而且我还是用线段树写的,如果改成树状数组估计时间空间编程复杂度还能进一步降低。。。。。。

 

下边上代码,这个方法来自于清华大学张昆玮大牛的ppt《统计的力量——线段树全接触》

 #include <iostream> #include <cstring> #include <cstdio> #include <cmath> using namespace std; const int maxc=100000+123; int M; long long a[maxc]; long long nsum[maxc*4]; long long summ[maxc*4]; int n,q; void change(long long *seg,int k,long long x) { k+=M; seg[k]+=x; while (k>1) { k>>=1; seg[k]=seg[k<<1]+seg[k<<1|1]; } } long long request(long long *seg,int r) { int l=M; r+=M+1; long long s=0; while (r>l+1) { if (!(l&1)) s+=seg[l+1]; if (r&1) s+=seg[r-1]; r>>=1;l>>=1; } return s; } long long getsum(int l,int r) { return (r+1)*request(summ,r)-request(nsum,r)-(l)*request(summ,l-1)+request(nsum,l-1); } void changein(int l,int r,int x) { change(summ,l,x); change(nsum,l,l*x); if (r+1<=n) { change(summ,r+1,-x); change(nsum,r+1,-((r+1)*x)); } } char order; int main() { long long i; while (scanf("%d%d",&n,&q)!=EOF) { int hh=ceil(log((n+2)*1.0)/log(2.0)); M=1<<hh; for (i=1;i<=n;i++) scanf("%lld",a+i); for (i=n;i>1;--i) a[i]=a[i]-a[i-1]; memset(nsum,0,sizeof(nsum)); memset(summ,0,sizeof(summ)); for (i=1;i<=n;i++) { change(summ,i,a[i]); change(nsum,i,i*a[i]); } int a,b; int c; while (q--) { cin>>order;scanf("%d%d",&a,&b); if (order=='Q') printf("%lld/n",getsum(a,b)); else { scanf("%d",&c); changein(a,b,c); } } } return 0; }

你可能感兴趣的:(编程,c,struct,tree,search,insert)