势能线段树(两道例题)

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);
        } 
    }
}
Code

 线段树开根号

传送门

题目描述

  • 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; 
} 
Code

 

posted @ 2021-09-13 00:02  lipu123  阅读(59)  评论(0)    收藏  举报