势能线段树(均摊分析)
勢能線段樹
線段樹能夠通過打懶標記實現區間修改的條件有兩個:
但有的區間修改不滿足上面兩個條件(如區間整除/開方/取模等)。
但某些修改存在一些奇妙的性質,使得序列每個元素被修改的次數有一個上限。
所以可以在線段樹每個節點上記錄一個值,表示對應區間內是否每個元素都達到修改次數上限。區間修改時暴力遞歸到葉子節點,如果途中遇到一個節點,這個節點的對應區間內每個元素都達到修改次數上限則在這個節點returnreturnreturn掉。
可以證明復雜度為 O((n+mlog?n)×lim)O((n+m\log n)\times lim)O((n+mlogn)×lim),其中nnn為序列長度,mmm為詢問次數,limlimlim為修改次數上限。
LOJ6029——線段樹區間除法
對于每個區間維護區間內的 最大值MaxMaxMax 和 最小值MinMinMin,對于整除操作,如果有Max??Maxd?=Min??Mind?Max-\lfloor\frac{Max}ze8trgl8bvbq\rfloor = Min ? \lfloor\frac{Min}ze8trgl8bvbq\rfloorMax??dMax??=Min??dMin??,就轉化為區間減。否則直接向下遞歸。
考慮何時滿足Max??Maxd?=Min??Mind?Max-\lfloor\frac{Max}ze8trgl8bvbq\rfloor = Min ? \lfloor\frac{Min}ze8trgl8bvbq\rfloorMax??dMax??=Min??dMin??:
設Max=k1d+c1,Min=k2d+c2(c1,c2∈[0,d?1],d≥2)Max=k_1d+c_1,Min=k_2d+c_2(c_1,c_2\in[0,d-1],d\geq 2)Max=k1?d+c1?,Min=k2?d+c2?(c1?,c2?∈[0,d?1],d≥2)
Max??Maxd?=Min??Mind?Max-\lfloor\frac{Max}ze8trgl8bvbq\rfloor=Min-\lfloor\frac{Min}ze8trgl8bvbq\rfloorMax??dMax??=Min??dMin??
Max?Min=?Maxd???Mind?Max-Min=\lfloor\frac{Max}ze8trgl8bvbq\rfloor-\lfloor\frac{Min}ze8trgl8bvbq\rfloorMax?Min=?dMax????dMin??
(k1?k2)d+c1?c2=k1?k2(k_1-k_2)d+c_1-c_2=k_1-k_2(k1??k2?)d+c1??c2?=k1??k2?
(k1?k2)(d?1)=c2?c1(k_1-k_2)(d-1)=c_2-c_1(k1??k2?)(d?1)=c2??c1?
若k1=k2,則c2=c1,所以Max=Min若k_1=k_2,則c_2=c_1,所以Max=Min若k1?=k2?,則c2?=c1?,所以Max=Min
若k1>k2,則(k1?k2)(d?1)≥d?1,又因為c2?c1≤d?1,所以當且僅當Max=k1d,Min=(k1?1)d+d?1時成立若k_1>k_2,則(k_1-k_2)(d-1)\geq d-1,又因為c_2-c_1\leq d-1,所以當且僅當Max=k_1d,Min=(k_1-1)d+d-1時成立若k1?>k2?,則(k1??k2?)(d?1)≥d?1,又因為c2??c1?≤d?1,所以當且僅當Max=k1?d,Min=(k1??1)d+d?1時成立
所以一個區間最多暴力向下遞歸log?2(Max?Min)\log_2(Max-Min)log2?(Max?Min)次。記勢能函數?(T)\phi(T)?(T)為線段樹TTT每一個節點log?(Max?Min)\log(Max?Min)log(Max?Min)的和,每一次修改操作會影響O(log?n)O(\log n)O(logn)個節點的Max?MinMax-MinMax?Min值,讓?(T)\phi(T)?(T)增加log?nlog?A\log n\log AlognlogA,其中AAA為值域。因此總時間復雜度為O(nlog?A+mlog?nlog?A)O(n\log A+m\log n\log A)O(nlogA+mlognlogA)。
UOJ228——線段樹區間開根
同上,對于每個區間維護區間內的 最大值MaxMaxMax 和 最小值MinMinMin,對于開根操作,如果有Max??Max?=Min??Min?Max-\lfloor\sqrt{Max}\rfloor = Min ? \lfloor\sqrt{Min}\rfloorMax??Max??=Min??Min??,就轉化為區間減。否則直接向下遞歸。
考慮何時滿足Max??Max?=Min??Min?Max-\lfloor\sqrt{Max}\rfloor = Min ? \lfloor\sqrt{Min}\rfloorMax??Max??=Min??Min??:
設Max=k12+c1,Min=k22+c2(c1∈[0,2k1];c2∈[0,2k2];k1,k2≥1)Max=k_1^2+c_1,Min=k_2^2+c_2(c_1\in[0,2k_1];c_2\in[0,2k_2];k_1,k_2\geq 1)Max=k12?+c1?,Min=k22?+c2?(c1?∈[0,2k1?];c2?∈[0,2k2?];k1?,k2?≥1)
Max??Max?=Min??Min?Max-\lfloor\sqrt{Max}\rfloor = Min ? \lfloor\sqrt{Min}\rfloorMax??Max??=Min??Min??
k12+c1?k1=k22+c2?k2k_1^2+c_1-k_1=k_2^2+c_2-k_2k12?+c1??k1?=k22?+c2??k2?
k12?k22?(k1?k2)=c2?c1k_1^2-k_2^2-(k_1-k_2)=c_2-c_1k12??k22??(k1??k2?)=c2??c1?
(k1+k2?1)(k1?k2)=c2?c1(k_1+k_2-1)(k_1-k_2)=c_2-c_1(k1?+k2??1)(k1??k2?)=c2??c1?
若k1=k2,則c2=c1,所以Max=Min若k_1=k_2,則c_2=c_1,所以Max=Min若k1?=k2?,則c2?=c1?,所以Max=Min
若k1>k2,則(k1+k2?1)(k1?k2)≥k1+k2?1≥2k2,又因為c2?c1≤2k2,所以當且僅當Max=k12,Min=(k1?1)2+2(k1?1)=k12?1時成立若k_1>k_2,則(k_1+k_2-1)(k_1-k_2)\geq k_1+k_2-1\geq 2k_2,又因為c_2-c_1\leq 2k_2,所以當且僅當Max=k_1^2,Min=(k_1-1)^2+2(k_1-1)=k_1^2-1時成立若k1?>k2?,則(k1?+k2??1)(k1??k2?)≥k1?+k2??1≥2k2?,又因為c2??c1?≤2k2?,所以當且僅當Max=k12?,Min=(k1??1)2+2(k1??1)=k12??1時成立
開根后,區間極差由Max?MinMax-MinMax?Min變為Max?Min\sqrt{Max}-\sqrt{Min}Max??Min?,又由Max?MinMax?Min=Max+Min>Max?Min\frac{Max-Min}{\sqrt{Max}-\sqrt{Min}}=\sqrt{Max}+\sqrt{Min}>\sqrt{Max}-\sqrt{Min}Max??Min?Max?Min?=Max?+Min?>Max??Min?知Max?Min>Max?Min\sqrt{Max-Min}>\sqrt{Max}-\sqrt{Min}Max?Min?>Max??Min?。
所以一個區間最多暴力向下遞歸 log?log?(Max?Min)\log\log(Max?Min)loglog(Max?Min) 次,總時間復雜度為O(nlog?log?A+mlog?nlog?log?A)O(n\log\log A+m\log n\log\log A)O(nloglogA+mlognloglogA),其中AAA為值域。
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> using namespace std; typedef long long ll; const int inf=1e9+7; const int N=1e5+10; ll mx[N<<2],mn[N<<2],sum[N<<2],tag[N<<2],a[N]; int n,q; ll Sqr(ll x){return sqrt(x); } void build(int u,int l,int r){tag[u]=0;if(l==r){mx[u]=mn[u]=sum[u]=a[l];return;}int mid=(l+r)>>1;build(u<<1,l,mid);build(u<<1|1,mid+1,r);mx[u]=max(mx[u<<1],mx[u<<1|1]);mn[u]=min(mn[u<<1],mn[u<<1|1]);sum[u]=sum[u<<1]+sum[u<<1|1]; } void pushdown(int u,int l,int r){if(tag[u]!=0){int mid=(l+r)>>1;mx[u<<1]+=tag[u];mn[u<<1]+=tag[u];sum[u<<1]+=tag[u]*(mid-l+1);tag[u<<1]+=tag[u];mx[u<<1|1]+=tag[u];mn[u<<1|1]+=tag[u];sum[u<<1|1]+=tag[u]*(r-mid);tag[u<<1|1]+=tag[u];tag[u]=0;} } void add(int u,int l,int r,int a,int b,ll x){if(a<=l&&r<=b){mx[u]+=x;mn[u]+=x;sum[u]+=x*(r-l+1);tag[u]+=x;return;}pushdown(u,l,r);int mid=(l+r)>>1;if(a<=mid) add(u<<1,l,mid,a,b,x);if(b>mid) add(u<<1|1,mid+1,r,a,b,x);mx[u]=max(mx[u<<1],mx[u<<1|1]);mn[u]=min(mn[u<<1],mn[u<<1|1]);sum[u]=sum[u<<1]+sum[u<<1|1]; } void sqr(int u,int l,int r,int a,int b){if(a<=l&&r<=b&&mx[u]-Sqr(mx[u])==mn[u]-Sqr(mn[u])){ll x=mx[u]-Sqr(mx[u]);mx[u]-=x;mn[u]-=x;sum[u]-=x*(r-l+1);tag[u]-=x;return;}pushdown(u,l,r);int mid=(l+r)>>1;if(a<=mid) sqr(u<<1,l,mid,a,b);if(b>mid) sqr(u<<1|1,mid+1,r,a,b);mx[u]=max(mx[u<<1],mx[u<<1|1]);mn[u]=min(mn[u<<1],mn[u<<1|1]);sum[u]=sum[u<<1]+sum[u<<1|1]; } ll Sum(int u,int l,int r,int a,int b){if(a<=l&&r<=b) return sum[u];pushdown(u,l,r);int mid=(l+r)>>1;ll res=0;if(a<=mid) res=res+Sum(u<<1,l,mid,a,b);if(b>mid) res=res+Sum(u<<1|1,mid+1,r,a,b);return res; } int main(){scanf("%d%d",&n,&q);for(int i=1;i<=n;i++) scanf("%lld",&a[i]);build(1,1,n);int opt,l,r;ll x;while(q--){scanf("%d%d%d",&opt,&l,&r);if(opt==1){scanf("%lld",&x);add(1,1,n,l,r,x);}else if(opt==2){sqr(1,1,n,l,r);}else if(opt==3){printf("%lld\n",Sum(1,1,n,l,r));}}return 0; }CF438D——線段樹區間取模
對于每個區間維護區間內的 最大值MaxMaxMax,對于整除操作,如果有Max<modMax<modMax<mod,就直接忽略。否則暴力向下遞歸。
可以證明,對于一個數xxx,最多取模log?x\log xlogx次就會令其變為1:
- 若mod>x2mod>\frac{x}{2}mod>2x?,那么x%mod=x?mod<x2x\%mod=x?mod<\frac{x}{2}x%mod=x?mod<2x?
- 若mod=x2mod=\frac{x}{2}mod=2x?,那么x%mod=0x\%mod=0x%mod=0
- 若mod<x2mod<\frac{x}{2}mod<2x?,那么x%mod<mod<x2x\%mod<mod<\frac{x}{2}x%mod<mod<2x?
總時間復雜度為(nlog?A+mlog?nlog?A)(n\log A+m\log n\log A)(nlogA+mlognlogA),其中AAA為值域。
CF920F——線段樹區間取約數個數
設D(x)D(x)D(x)表示xxx的約數個數。
發現D(x)D(x)D(x)有以下性質:
那么一個數xxx的修改次數上限為log?log?x\log\log xloglogx。
套用上面的方法即可。
總結
以上是生活随笔為你收集整理的势能线段树(均摊分析)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 苹果为老款机型推出 iOS 16.7.2
- 下一篇: 怎么折三角纸