CF679E 题解

题意简述

我们称 $42$ 的正整数次幂为“坏数”(正整数次幂,不包含 $1$),而其他的正整数都是“好数”。

给出长度为 $n(1\le n\le10^5)$ 的序列 $a_i(1\le a_i\le10^9)$,并进行 $q(1\le q\le 10^5)$ 次操作。操作共三种:

  1. 给定 $x$,求 $a_x$ 的值。

  2. 给定 $l,r$,将区间 $a_l,a_{l+1},\cdots,a_{r-1},a_r$ 全赋值为给定的 $x(1\le x\le10^9)$。

  3. 给定 $l,r$ 和 $x(1\le x\le10^9)$。先将 $a_l,a_{l+1},\cdots,a_{r-1},a_r$ 全部加上 $x$,然后不断如此操作下去一直到 $a_l,a_{l+1},\cdots,a_{r-1},a_r$ 都是“好数”为止。

题目分析

显然操作三并不能不停操作下去(操作有限次之后一定能找到两个相邻的 $42$ 的次幂使得 $a_l\sim a_r$ 都夹在其之间,证明此处不多赘述),这就表明可能涉及到的 $42$ 的次幂也是有限的。具体来说,最大的 $42$ 的次幂在 $O(qx)$ 级别,也就是 $10^{14}$ 左右,不会超过 $42^9≈4\times10^{14}$。可以直接把 $42^1,42^2,\cdots,42^9$ 即所谓“坏数”预处理一下。

考虑使用线段树维护 $a_l\sim a_r$ 之间每个 $a_i$ 到下一个“坏数”距离的最小值。操作二的区间赋值很简单,打懒标记简单处理,更新最小值直接 lower_bound 查找新的“坏数”计算就行(直接枚举也行);操作三则先实现区间加,然后不断区间加直到 $a_1\sim a_n$ 距“坏数”距离最小值不为 $0$ 即可。而单点查询,单凭一个数距“坏数”的距离不能确定其原始值,但是区间赋值标记可以解决这一问题。我们直接让 $l=r$ 的结点的区间赋值标记记录原始值即可。

至于时间复杂度,根据刚刚的分析,每个位置只会被修改 $O(\log_{42}qx)$ 次,所以总时间复杂度为 $O((n+q)\log_2n\log_{42}qx)$

代码实现

#include<bits/stdc++.h>
using namespace std;
int n,q,op,l,r;
long long pow42[12],a[100010],x;
struct node
{
    int l,r;//左右端点。 
    long long mn,tag1,tag2;//mn:离下一个“坏数”距离的最小值;tag1 是区间加标记,tag2 是区间赋值标记。 
}tr[400010];
template<typename T>void rd(T &x)
{
    x=0;
    char c=getchar();
    for(;c>'9'||c<'0';c=getchar());
    for(;c<='9'&&c>='0';c=getchar())
        x=(x<<3)+(x<<1)+c-'0';
}//快读 
template<typename T>void wt(T x)
{
    if(x>=10)
        wt(x/10);
    putchar(x%10+'0');
}//快写 
long long calc(long long x)
{
    return (*lower_bound(pow42+1,pow42+11,x))-x;//距下一个“坏数”的距离 
}
void pushup(int p)
{
    tr[p].mn=min(tr[p<<1].mn,tr[p<<1|1].mn);
}//子结点更新父结点 
void addtag1(int p,long long tag)
{
    if(tr[p].tag2)//区间赋值过就加到标记上 
    {
        tr[p].tag2+=tag;
        tr[p].mn=calc(tr[p].tag2);
    } 
    else//没区间赋值过就直接打标记 
    {
        tr[p].mn-=tag;
        tr[p].tag1+=tag;
    }
}//区间加打标记 
void addtag2(int p,long long tag)
{
    tr[p].tag2=tag;
    tr[p].mn=calc(tag);
    tr[p].tag1=0;//区间赋值之后,之前的区间加都失效 
}//区间赋值打标记 
void pushdown(int p)
{
    if(tr[p].tag1)
    {
        addtag1(p<<1,tr[p].tag1);
        addtag1(p<<1|1,tr[p].tag1);//向左右子结点下传标记 
        tr[p].tag1=0;//清空标记 
    }
    if(tr[p].tag2)
    {
        addtag2(p<<1,tr[p].tag2);
        addtag2(p<<1|1,tr[p].tag2);//向左右子结点下传标记 
        tr[p].tag2=0;//清空标记 
    }
}
void build(int p,int l,int r)//建树 
{
    tr[p].l=l,tr[p].r=r;
    if(l==r)
    {
        addtag2(p,a[l]);//为了方便询问,初始化就添上一个赋值标记 
        return;
    }
    int mid=l+r>>1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
    pushup(p);
}
void change1(int p,int l,int r,int x)//区间加 
{
    if(tr[p].l>=l&&tr[p].r<=r/*区间全包含*/&&(tr[p].tag2||tr[p].mn>=x)/*若被区间赋值过或者区间加操作完后,没有比原来的“坏数”大,那么就可以直接打标记*/)
    {
        addtag1(p,x);
        return;
    }
    //否则就递归下去算 
    pushdown(p);
    int mid=tr[p].l+tr[p].r>>1;
    if(mid>=l)
        change1(p<<1,l,r,x);
    if(mid<r)
        change1(p<<1|1,l,r,x);
    pushup(p);
}
void change2(int p,int l,int r,int x)//区间赋值 
{
    if(tr[p].l>=l&&tr[p].r<=r)
    {
        addtag2(p,x);
        return;
    }
    pushdown(p);
    int mid=tr[p].l+tr[p].r>>1;
    if(mid>=l)
        change2(p<<1,l,r,x);
    if(mid<r)
        change2(p<<1|1,l,r,x);
    pushup(p);
}
long long query(int p,int x)//单点查询,也可以直接二分实现 
{
    if(tr[p].l==tr[p].r)
        return tr[p].tag2;
    int mid=tr[p].l+tr[p].r>>1;
    pushdown(p);
    if(mid>=x)
        return query(p<<1,x);
    else
        return query(p<<1|1,x);
}
int main()
{
    pow42[0]=1ll;
    for(int i=1;i<=10;i++)
        pow42[i]=42ll*pow42[i-1];//预处理“坏数” 
    rd(n),rd(q);
    for(int i=1;i<=n;i++)
        rd(a[i]);
    build(1,1,n);
    while(q--)
    {
        rd(op),rd(l);
        switch(op)
        {
            case 1:
                wt(query(1,l));
                putchar('\n');
                break;
            case 2:
                rd(r),rd(x);
                change2(1,l,r,x);   
                break;
            case 3:
                rd(r),rd(x);
                do
                {
                    change1(1,l,r,x);
                }while(tr[1].mn==0);//由于至少要操作一次,所以用 do-while。 
        }
    }
    return 0;
}
posted @ 2023-08-16 17:07  Hadtsti  阅读(13)  评论(0编辑  收藏  举报  来源