势能线段树(两道例题)
CodeForces - 438D (线段树区间取余)
题目描述
给一个序列
支持3种操作
1 u v 对于所有i u<=i<=v, 输出a[i]的和
2 u v t 对于所有i u<=i<=v a[i]=a[i]%t
3 u v 表示a[u]=v (将v赋值给a[u])
n,q<=1e5 a[i],t,v<=1e9
题目解析
有一个很经典的结论:
a mod p< a/2(a>p)
有了这个结论后我们就很容易得出一个数 x 最多被取余 logx 次,因此我们可以仿照区间开方的思路对于维护区间最大值,然后在最大值小于 p 时再暴力取余即可
Code

#include<iostream> #include<algorithm> using namespace std; typedef long long ll; const int maxn=1e6+100; ll a[maxn]; struct node{ int l,r; ll sum,ma; }t[maxn]; void push(int p){ t[p].sum=(t[2*p].sum+t[2*p+1].sum); t[p].ma=max(t[2*p].ma,t[2*p+1].ma); } void build(int p,int l,int r){ t[p].l=l; t[p].r=r; if(t[p].l==t[p].r){ t[p].ma=t[p].sum=a[l]; return ; } int mid=(t[p].l+t[p].r)/2; build(2*p,l,mid); build(2*p+1,mid+1,r); push(p); } void updatemod(int p,int l,int r,int k){ if(t[p].ma<k){ return ; } if(t[p].l==t[p].r){ t[p].sum=t[p].sum%k; t[p].ma=t[p].ma%k; return ; } int mid=(t[p].l+t[p].r)/2; if(l<=mid){ updatemod(2*p,l,r,k); } if(r>mid){ updatemod(2*p+1,l,r,k); } push(p); } void update(int p,int x,int k){ if(t[p].l==t[p].r){ t[p].ma=t[p].sum=k; return ; } int mid=(t[p].l+t[p].r)/2; if(x<=mid){ update(2*p,x,k); } else{ update(2*p+1,x,k); } push(p); } ll query(int p,int l,int r){ if(t[p].l>=l&&t[p].r<=r){ return t[p].sum; } ll ans=0; int mid=(t[p].l+t[p].r)/2; if(l<=mid){ ans+=query(2*p,l,r); } if(r>mid){ ans+=query(2*p+1,l,r); } return ans; } int main(){ int n,m; cin>>n>>m; for(int i=1;i<=n;i++){ cin>>a[i]; } build(1,1,n); int op,x,y,z; while(m--){ scanf("%d%d%d",&op,&x,&y); if(op==1){ printf("%lld\n",query(1,x,y)); } else if(op==2){ scanf("%d",&z); updatemod(1,x,y,z); } else if(op==3){ update(1,x,y); } } }
线段树开根号
题目描述
- k=0 表示给 [l,r]中的每个数开平方(下取整)。
- k=1 表示询问 [l,r]中各个数的和。
题目解析
- 显然,这道题的求和操作可以用线段树来维护
- 但是如何来实现区间开方呢
- 大家有没有这样的经历:玩计算器的时候,把一个数疯狂的按开方,最后总会变成 1,之后在怎样开方也是 1(sqrt1=1
- 同样的,sqrt0=0
- 所以,只要一段区间里的所有数全都 ≤1 了,便可以不去修改它
Code

#include<iostream> #include<algorithm> #include<math.h> using namespace std; typedef long long ll; template <typename Tp> void read(Tp &x){//read(n); x=0;char ch=1;int fh; while(ch!='-'&&(ch>'9'||ch<'0')){ ch=getchar(); } if(ch=='-'){ fh=-1;ch=getchar(); }else fh=1; while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+ch-'0';ch=getchar(); } x*=fh; } const int maxn=5e6+100; struct node{ int l,r; ll sum; }t[maxn]; ll a[maxn]; void jianshu(int p,int l,int r){ t[p].l=l; t[p].r=r; if(l==r){ t[p].sum=a[l]; return ; } int mid=(t[p].l+t[p].r)>>1; jianshu(p<<1,l,mid); jianshu(p<<1|1,mid+1,r); t[p].sum=t[p<<1].sum+t[p<<1|1].sum; } void update(int p,int l,int r){ int L=t[p].l,R=t[p].r; if(L==R){ t[p].sum=sqrt(1.0*t[p].sum); return ; } if((R-L+1)>=t[p].sum){//区间内都是1 重点 return ; } if(l<=t[p<<1].r){ update(p<<1,l,r); } if(r>=t[p<<1|1].l){ update(p<<1|1,l,r); } t[p].sum=t[p<<1].sum+t[p<<1|1].sum; } ll query(int p,int l,int r){ if(t[p].l>=l&&t[p].r<=r){ return t[p].sum; } ll ans=0; if(l<=t[p<<1].r){ ans+=query(2*p,l,r); } if(r>=t[p<<1|1].l){ ans+=query(p<<1|1,l,r); } return ans; } int main(){ int n,m; cin>>n; for(int i=1;i<=n;i++){ read(a[i]); } jianshu(1,1,n); read(m); int op,l,r; for(int i=1;i<=m;i++){ read(op),read(l),read(r); if(r<l){ swap(l,r); } if(op==0){ update(1,l,r); } else{ printf("%lld\n",query(1,l,r)); } } return 0; }