2018 Multi-University Training Contest hdu 6315 Naive Operations(線段樹)
阿新 • • 發佈:2018-11-10
題意
有兩個數列a和b,a的初始值為0,b陣列是1~n的一個排列,有兩個操作:
1. 將a陣列區間[l,r]的每個數+1。
2. 輸出Σa[i]/b[i](l<=i<=r)Σa[i]/b[i](l<=i<=r)。
題解
由於運算元一共10^5,所以對於每一個位置i的貢獻,我們頂多更新n/b[i]次,所以總的更新次數是n*log(n),我們通過線段樹進行區間所缺數最大值的維護。詢問時,假如詢問區間內,線段樹上當前區間的所缺數最大值>=0,說明需要更新答案,然後找到需要更新的葉子節點進行更新,所以每次更新的複雜度為O(log(n)),因此演算法的總複雜度為O(n*logn*logn)。
程式碼
#include<bits/stdc++.h> #define N 100005 #define P pair<int,int> using namespace std; typedef long long ll; const int M=1e9+7; const int inf=1e9+7; int b[N],tree[N*4],rev[N*4],add[N*4]; void pushup(int p) { tree[p]=tree[p<<1]+tree[p<<1|1]; rev[p]=max(rev[p<<1],rev[p<<1|1]); } void pushdown(int p) { if(!add[p])return; rev[p<<1]+=add[p]; rev[p<<1|1]+=add[p]; add[p<<1]+=add[p]; add[p<<1|1]+=add[p]; add[p]=0; } void build(int l,int r,int p) { tree[p]=add[p]=0; if(l==r){ rev[p]=-b[l]; return; } int mid=l+r>>1; build(l,mid,p<<1); build(mid+1,r,p<<1|1); pushup(p); } void update(int l,int r,int p) { if(l==r){ int t=rev[p]/b[l]+1; tree[p]+=t; rev[p]-=t*b[l]; return; } pushdown(p); int mid=l+r>>1; if(rev[p<<1]>=0)update(l,mid,p<<1); if(rev[p<<1|1]>=0)update(mid+1,r,p<<1|1); pushup(p); } void update(int l,int r,int L,int R,int p) { if(l<=L&&R<=r){ add[p]++; rev[p]++; return; } pushdown(p); int mid=L+R>>1; if(l<=mid)update(l,r,L,mid,p<<1); if(r>mid)update(l,r,mid+1,R,p<<1|1); pushup(p); } int query(int l,int r,int L,int R,int p) { if(l<=L&&R<=r){ if(rev[p]>=0)update(L,R,p); return tree[p]; } pushdown(p); int mid=L+R>>1; int ans=0; if(l<=mid)ans+=query(l,r,L,mid,p<<1); if(r>mid)ans+=query(l,r,mid+1,R,p<<1|1); return ans; } int main() { int n,q; while(~scanf("%d%d",&n,&q)) { for(int i=1;i<=n;i++) scanf("%d",&b[i]); build(1,n,1); char c[10]; int l,r; while(q--) { scanf("%s%d%d",c,&l,&r); if(c[0]=='a')update(l,r,1,n,1); else printf("%d\n",query(l,r,1,n,1)); } } return 0; }