【UOJ228】基础数据结构练习题(线段树)

Description

    sylvia 是一个热爱学习的女孩子,今天她想要学习数据结构技巧。
    在看了一些博客学了一些姿势后,她想要找一些数据结构题来练练手。于是她的好朋友九条可怜酱给她出了一道题。
    给出一个长度为 nn 的数列 AA,接下来有 mm 次操作,操作有三种:

对于所有的 i∈[l,r]i∈[l,r],将 Ai变成 Ai+x。
对于所有的 i∈[l,r]i∈[l,r],将 Ai变成 ⌊√Ai⌋。
对于所有的 i∈[l,r]i∈[l,r],询问 Ai的和。

    作为一个不怎么熟练的初学者,sylvia 想了好久都没做出来。而可怜酱又外出旅游去了,一时间联系不上。于是她决定向你寻求帮助:你能帮她解决这个问题吗。

Input

    第一行两个数:n,m。
    接下来一行 n个数 Ai。
    接下来 m行中,第 i 行第一个数 ti 表示操作类型:
    若 ti=1 ,则接下来三个整数 li,ri,xi ,表示操作一。
    若 ti=2 ,则接下来三个整数 li,ri ,表示操作二。
    若 ti=3 ,则接下来三个整数 li,ri ,表示操作三。

Output

    对于每个询问操作,输出一行表示答案。

Sample Input

5 5
1 2 3 4 5
1 3 5 2
2 1 4
3 2 4
2 3 5
3 1 5

Sample Output

5
6

Data

    对于所有数据,保证有 1lirin,1Ai,xi105

I think

    我们来搞第二个操作。
    单点修改变成 ⌊√Ai⌋是会TLE的。但是对于每一个数n,同样可以在近 log(n) 的时间里变为1,因此我们也可以用类似于【hdu5634】Rikka with Phi(线段树+欧拉函数) 的方法合并节点,不同的是本题有+x的操作。
    我们维护区间最大最小值mx[i],mn[i],当一个区间满足

mx[i]==mn[i]
mx[i]mn[i]==floor(sqrt(mx[i]))floor(sqrt(mn[i]))

    这个区间内的每个元素开根号之后的变化量相同,于是我们就可以很方便地更新了。
    在传递添加值x时,尽管 x<=105 ,但用x更新s[i]时需要 *1ll避免溢出。

Code

#include<cmath>
#include<cstdio>
using namespace std;
const int sm = 8e5+50;
typedef long long LL;
int v,n,m;
LL s[sm],a[sm],mx[sm],mn[sm];
char ch;
template <typename T> T Max(T x,T y) { return x>y?x:y; }
template <typename T> T Min(T x,T y) { return x<y?x:y; } 
template <typename T> void read(T &x) {
    x=0,ch=getchar();
    while(ch>'9'||ch<'0') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
void update(int i) {
    s[i]=s[i<<1]+s[i<<1|1];
    mx[i]=Max(mx[i<<1],mx[i<<1|1]);
    mn[i]=Min(mn[i<<1],mn[i<<1|1]);
}
void pd(int i,int l,int r) {
    int ls=i<<1,rs=i<<1|1,m=(r+l)>>1;
    a[ls]+=a[i],a[rs]+=a[i];
    s[ls]+=(m-l+1)*a[i],s[rs]+=(r-m)*a[i];
    mx[ls]+=a[i],mn[ls]+=a[i];
    mx[rs]+=a[i],mn[rs]+=a[i];
    a[i]=0;
}
void build(int i,int l,int r) {
    if(l==r) {
        read(v),s[i]=mx[i]=mn[i]=v;
        return ;
    }
    int m=(l+r)>>1;
    build(i<<1,l,m);
    build(i<<1|1,m+1,r);
    update(i);
}
void add(int i,int l,int r,int ll,int rr,int val) {//
    if(ll<=l&&r<=rr) {
        a[i]=1ll*(a[i]+val),s[i]=s[i]+1ll*(r-l+1)*val;
        mx[i]=1ll*(mx[i]+val),mn[i]=1ll*(mn[i]+val);
        return ;
    }
    if(a[i])pd(i,l,r);
    int m=(l+r)>>1;
    if(ll<=m) add(i<<1,l,m,ll,rr,val);
    if(rr> m) add(i<<1|1,m+1,r,ll,rr,val);
    update(i);
}
bool chk(int i) {
    return (mx[i]==mn[i])||(mx[i]-mn[i]==floor(sqrt(mx[i]))-floor(sqrt(mn[i])));
}
void modify(int i,int l, int r,int ll,int rr) {
    if(ll<=l&&r<=rr&&chk(i)) {
        LL d=floor(sqrt(mx[i]))-mx[i];
        mx[i]+=d,mn[i]+=d;
        a[i]+=d,s[i]+=(r-l+1)*d;
        return;
    }
    if(a[i])pd(i,l,r);
    int m=(l+r)>>1;
    if(ll<=m) modify(i<<1,l,m,ll,rr);
    if(rr> m) modify(i<<1|1,m+1,r,ll,rr);
    update(i);
}
LL query(int i,int l,int r,int ll,int rr) {
    if(ll<=l&&r<=rr) return s[i];
    if(a[i])pd(i,l,r);
    int m=(l+r)>>1;LL ans=0;
    if(ll<=m) ans+=query(i<<1,l,m,ll,rr);
    if(rr> m) ans+=query(i<<1|1,m+1,r,ll,rr);
    update(i);
    return ans;
}
int main() {
    read(n),read(m);
    build(1,1,n);
    int ind,l,r,x;
    for(LL i=1;i<=m;++i) {
        read(ind),read(l),read(r);
        if(ind==1) read(x),add(1,1,n,l,r,x);
        else if(ind==2) modify(1,1,n,l,r);
        else printf("%lld\n",query(1,1,n,l,r));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值