CF311D Interval Cubing 数学、线段树

CF311D Interval Cubing 数学、线段树

数学太重要了。。有些人知道点结论就喜欢搞个数学包装水题,欺负吾等数学学渣啊。。。



CF有官方题解,不过我还是记录一下吧。
这是个线段树的经典题目,记录某段被更新了多少次(lazy tag)、求区段和。

由于每个点的状态只有0到47,在建树的时候就把每个点的状态都求好,记录一个偏移量,每次该区间的实际值就是该偏移量的状态(t[now].sum[cnt])。

要注意更新的时候要从下到上都更新一遍。复杂度是O(n*T+q*T*log(n)),CF的机子就是快。。
然后就很简单了。


什么,最重要的还没说?

什么费马小定理啊,因为a^(p-1)%p==1,这样a^x==a^(x%(p-1)),
还有神马周期性啊,因为(3^48)%(95542721-1)==1,靠这也能看出来?好吧,我知道这叫经验。

这样a^(3^x)==a^(3^(x%48)),也就是说每个点的状态只有ai,ai^3..ai^(3^47)而且以周期为48循环的。


脑抽线段树某处写错TLE无数次。。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define ls (now<<1)
#define rs (now<<1|1)
#define NN 101000
#define f(x) ((long long)x*x%mod*x%mod)
#define mm(x) (x>=48?(x-48):x)
#define md(x) while(x>=48) x-=48;
const int mod=95542721;

int n,a[NN];
int tcnt;

struct segnode{
    int l,r;
    int sum[50];
    int cnt,add;
}t[NN*4];

void build(int l,int r,int now){
    int i;
    t[now].l=l;t[now].r=r;
    t[now].cnt=t[now].add=0;
    if (l==r){
        t[now].sum[0]=a[l];
        for(i=1;i<48;++i){
            t[now].sum[i]=f(t[now].sum[i-1]);
        }
        return;
    }
    int mid=(l+r)/2;
    build(l,mid,ls);
    build(mid+1,r,rs);
    for(i=0;i<48;++i) t[now].sum[i]=(t[ls].sum[i]+t[rs].sum[i])%mod;
}

void lazy(int now){
    if (t[now].add==0||t[now].l==t[now].r) return;
    int k=t[now].add;
    t[ls].cnt+=k;t[ls].add+=k;
    t[rs].cnt+=k;t[rs].add+=k;
    md(t[ls].cnt);md(t[ls].add);
    md(t[rs].cnt);md(t[rs].add);
    t[now].add=0;
}

void update(int l,int r,int now){
    int i;
    if (t[now].l==l&&t[now].r==r){
        t[now].add+=1;
        t[now].cnt+=1;
        md(t[now].cnt);md(t[now].add);
        return;
    }
    lazy(now);
    int mid=(t[now].l+t[now].r)/2;
    if (r<=mid){update(l,r,ls);}
    else if (l>mid) {update(l,r,rs);}
    else {update(l,mid,ls);update(mid+1,r,rs);}
    t[now].cnt=0;
    for(i=0;i<48;++i) t[now].sum[i]=(t[ls].sum[mm(i+t[ls].cnt)]+t[rs].sum[mm(i+t[rs].cnt)])%mod;
}

int query(int l,int r,int now){
    if (t[now].l==l&&r==t[now].r){
        return t[now].sum[mm(t[now].cnt)];
    }
    lazy(now);
    int mid=(t[now].l+t[now].r)/2;
    if (r<=mid) return query(l,r,ls);
    else if (l>mid) return query(l,r,rs);
    else {return (query(l,mid,ls)+query(mid+1,r,rs))%mod;}
}



int main(){
    //freopen("311Din.txt","r",stdin);
    int i,aa,bb,o,q;
    scanf("%d",&n);
    for(i=1;i<=n;++i){
        scanf("%d",&a[i]);
    }
    build(1,n,1);
    scanf("%d",&q);
    for(i=1;i<=q;++i){
        scanf("%d%d%d",&o,&aa,&bb);
        if (o==1) {printf("%d\n",query(aa,bb,1));}
        else {update(aa,bb,1);}
    }
    return 0;

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值