HDU 6315 Naive Operations(線段樹+複雜度均攤)
阿新 • • 發佈:2018-12-20
發現每次區間加只能加1,最多全域性加\(n\)次,這樣的話,最後的答案是調和級數為\(nlogn\),我們每當答案加1的時候就單點加,最多加\(nlogn\)次,複雜度可以得當保證。
然後問題就是怎麼判斷答案是否該加1。我們可以用線段樹設初值為給出的排列,把區間加改成區間減,維護最小值。當最小值為0是遍歷左右子樹,找到該加1的節點,一共會找\(nlongn\)次複雜度也可以得到保證。
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<algorithm> using namespace std; const int N=101001; int n,a[N],ned[N*9],lazy[N*9],tr[N],m; int read(){ int sum=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();} return sum*f; } int lowbit(int x){ return x&-x; } void add(int x,int c){ for(int i=x;i<=n;i+=lowbit(i))tr[i]+=c; } int getsum(int x){ int tmp=0; for(int i=x;i;i-=lowbit(i))tmp+=tr[i]; return tmp; } void update(int now){ ned[now]=min(ned[now*2],ned[now*2+1]); } void build(int l,int r,int now){ lazy[now]=0; if(l==r){ ned[now]=a[l]; return; } int mid=(l+r)>>1; build(l,mid,now*2); build(mid+1,r,now*2+1); update(now); } void pushdown(int l,int r,int now){ if(lazy[now]==0)return; if(l==r)return; ned[now*2]+=lazy[now]; ned[now*2+1]+=lazy[now]; lazy[now*2]+=lazy[now]; lazy[now*2+1]+=lazy[now]; lazy[now]=0; } void add(int l,int r,int L,int R,int now){ pushdown(l,r,now); if(l==L&&r==R){ if(ned[now]==1){ if(l==r){add(l,1);ned[now]=a[l];return;} int mid=(l+r)>>1; add(l,mid,L,mid,now*2); add(mid+1,r,mid+1,R,now*2+1); update(now); return; } lazy[now]--; ned[now]--; return; } int mid=(l+r)>>1; if(L>mid)add(mid+1,r,L,R,now*2+1); else if(R<=mid)add(l,mid,L,R,now*2); else add(l,mid,L,mid,now*2),add(mid+1,r,mid+1,R,now*2+1); update(now); } int main(){ while(scanf("%d%d",&n,&m)!=EOF){ for(int i=1;i<=n;i++)a[i]=read(); memset(tr,0,sizeof(tr)); build(1,n,1); char s[10]; while(m--){ scanf("%s",s); int l=read(),r=read(); if(s[0]!='a'){ printf("%d\n",getsum(r)-getsum(l-1)); } else add(1,n,l,r,1); } } return 0; }