[HZOI 2016]數列操作d
阿新 • • 發佈:2018-03-29
roo pen font HA 線段樹 高速公路 思想 標記 區間
我的第一道題解就寫它吧。
維護區間和+花式修改。用線段樹或者樹狀數組可以解決,但是我沒怎麽寫過樹狀數組。
維護和的操作直接把左右子樹和加起來。
重點是修改。
去年剛學完線段樹刷完數列操作a,b,c後看道這題就棄了。現在知道了關鍵就是推式子,跟HAOI2012高速公路是一個套路的。
線段樹就是用於維護區間的,而且因為延遲標記的存在,所以我們先考慮區間增量。
對於區間 [l,r] 增量為 Σri=l(i-L)*x。L是總的修改範圍,l,r,是線段樹中節點的範圍,第一次就是因為沒註意這兩者的關系,導致公式錯誤連樣例都過不了。
提公因式x,Σ裏是等差數列直接求和之後相乘就算出增量了,這時候要考慮如何打lazy標記。
思想也是提公因式。
Σri=l ( i-L ) * x = ( Σri=l i ) * x - ( Σri=l1 ) * L * x
設 A = Σri=l i,B = Σri=l1 ,可以發現對於線段樹中的每段區間A和B的值是固定的,這樣一來我們只需要累計每次修改的x以及L*x的值就可以順利下傳延遲修改了。
這道題就順利解決了。
// q.c #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; typedef long long LL; const int M=300000+10; const int mod=(int)1e9+7; struct Node { int l,r,sum,s1,s2; Node():l(0),r(0),sum(0),s1(0),s2(0) {} }; struct SegmentTree { int root; Node nd[M<<2]; SegmentTree():root(1) {} void update(int o) { nd[o].sum=((LL)nd[o<<1].sum+nd[o<<1^1].sum)%mod; } void pushdown(int o) { Node &p=nd[o],&lc=nd[o<<1],&rc=nd[o<<1^1]; lc.sum=(lc.sum+(LL)p.s1*(lc.r+lc.l)*(lc.r-lc.l+1)/2)%mod; lc.sum=(lc.sum-(LL)p.s2*(lc.r-lc.l+1)%mod+mod)%mod; lc.s1=(lc.s1+p.s1)%mod; lc.s2=(lc.s2+p.s2)%mod; rc.sum=(rc.sum+(LL)p.s1*(rc.r+rc.l)*(rc.r-rc.l+1)/2)%mod; rc.sum=(rc.sum-(LL)p.s2*(rc.r-rc.l+1)%mod+mod)%mod; rc.s1=(rc.s1+p.s1)%mod; rc.s2=(rc.s2+p.s2)%mod; p.s1=p.s2=0; } void build(int o,int l,int r) { nd[o].l=l,nd[o].r=r; if(l!=r) { int mid=(l+r)>>1; build(o<<1,l,mid); build(o<<1^1,mid+1,r); } } void add(int o,int l,int r,int x) { Node &p=nd[o]; if(l<=p.l&&p.r<=r) { p.sum=(p.sum+(LL)x*(p.r-l+p.l-l)*(p.r-p.l+1)/2)%mod; p.s1=(p.s1+x)%mod; p.s2=(p.s2+(LL)l*x)%mod; } else { if(p.s1||p.s1) pushdown(o); int mid=(p.l+p.r)>>1; if(l<=mid) add(o<<1,l,r,x); if(r>mid) add(o<<1^1,l,r,x); update(o); } } int query(int o,int l,int r) { Node p=nd[o]; if(l<=p.l&&p.r<=r) return p.sum; else { if(p.s1||p.s2) pushdown(o); int mid=(p.l+p.r)>>1,ans=0; if(l<=mid) ans=((LL)ans+query(o<<1,l,r))%mod; if(r>mid) ans=((LL)ans+query(o<<1^1,l,r))%mod; return ans; } } }t; int n,m; int main() { freopen("segment.in","r",stdin); freopen("segment.out","w",stdout); scanf("%d%d",&n,&m); t.build(t.root,1,n); int opt,l,r,x; for(int i=1;i<=m;i++) { scanf("%d%d%d",&opt,&l,&r); if(opt) scanf("%d",&x),t.add(t.root,l,r,x); else printf("%d\n",t.query(t.root,l,r)); } return 0; }
公式真的好難打啊。
[HZOI 2016]數列操作d