P7811 [JRKSJ R2] 你的名字。

Links#

P7811 [JRKSJ R2] 你的名字。

LuoguBlog


题外话#

  • 纪念一下 300 蓝紫

  • 最开始看到这题的时候没做出来,今天突然就会了,看来写大分块还是有点用的

  • 第一次写分块套 ST(虽然不是第一次有这个想法,但以前只口胡过),还是写一下

  • ARF 我的神(乱入


题意#

求区间 [l,r]ai 在模 k 意义下的最小值。

1n,m3×105,1V105V 为值域)

1s,128MB


题解#

取模,根号分治,设定阈值 T。下面认为 n,m 同阶。

kT 时,直接做 T 次暴力取模,然后问题就变成了 RMQ,此处 RMQ 选择了用分块实现。时间复杂度 O(nT+nn)

k>T 时,我们可以暴力跳 k 的倍数(设为 x),每个 k 只会跳 O(VT) 次,总共 O(nVT) 次,然后此时的答案应是 min{ai|liraix}x,即 O(nVT) 次查询区间内 x 的非严格后继。

感觉这个查询次数大概只能支持 O(1) 查询,但是求区间内非严格后继这个东西看起来不太好实现 O(1),考虑转化一下。

我们把这些询问离线下来,将 ai 从大到小插入,同时从大到小地去 solve x,这样问题就变成了 O(n) 次修改,O(nVT) 查询区间最小值,就好做得多了。

线段树、树状数组啥的都不能 O(1) 查询,分块才有前途。但是 min 是不可差分信息,我们不能像分块维护可差分信息那样维护块内前后缀信息和块间的前缀信息来做到 O(1) 查询。但是维护块内前后缀信息还是可以保留的,块间要做到 O(1) 查询,我们想到了 ST。用 ST 维护整块的信息可以做到 O(n) 修改,O(1) 查询,很符合我们的需求。

但是 128MB 的空间不允许我们把全部的 O(nVT)x 保存下来,考虑按 k 把它们整合一下。前面我们是枚举 k 的倍数 x,这里我们反过来,当把 ai 插入的过程进行到 x 的时候,枚举 x 的因数 k,然后计算答案就好。

此部分时间复杂度为 O(nn+nVT)

T=V 以达到平衡,总时间复杂度为 O(n(n+V)),空间线性。


Code#

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define il inline
#define re register
const int N=3e5+5,V=1e5,sq=1e3,len=550,T=500,star=1e9;
int n,m,id[N],L[sq],R[sq],st[10][sq];
int a[N],ans[N],b[N],mn[sq],pre[N],suf[N];
#define Log(x) (31^__builtin_clz(x))
#define pb emplace_back
bitset<V+10>s1,s2;
bitset<N>vis;
il int read(){
    re int x=0;re char c=getchar(),f=0;
    while(c<'0'||c>'9') f|=(c=='-'),c=getchar();
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c&15),c=getchar();
    return f?-x:x;
}
#define tii tuple<int,int,int>
#define mkt make_tuple
vector<tii>q1[V+5],q2[V+5];
vector<int>d[V+10],v[V+10];
il int GetMin_B(int l,int r){
    re int res=star;
    if(id[l]==id[r]){
        for(re int i=l;i<=r;i++)res=min(res,b[i]);
        return res;
    }
    for(re int i=l;i<=R[id[l]];i++)res=min(res,b[i]);
    for(re int i=L[id[r]];i<=r;i++)res=min(res,b[i]);
    for(re int i=id[l]+1;i<id[r];i++)res=min(res,mn[i]);
    return res;
}
il void Solve1(){
    for(re int k=s1._Find_first();k<=V;k=s1._Find_next(k)){
        memset(mn,0x3f,sizeof mn);
        for(re int i=1;i<=n;i++)b[i]=a[i]%k,mn[id[i]]=min(mn[id[i]],b[i]);
        for(tii i:q1[k])ans[get<2>(i)]=GetMin_B(get<0>(i),get<1>(i));
    }
}
il void Get_d(){
    for(re int i=s2._Find_first();i<=V;i=s2._Find_next(i))
        for(re int j=0;j<=V;j+=i)d[j].pb(i);
}
il void update(int x){
    for(re int i=L[id[x]];i<=x;i++)suf[i]=min(suf[i],a[x]);
    for(re int i=R[id[x]];i>=x;i--)pre[i]=min(pre[i],a[x]);
    for(re int i=0;i<=Log(id[n]);i++)
        for(re int j=max(id[x]-(1<<i)+1,1);j<=min(id[x],id[n]-(1<<i)+1);j++)
            st[i][j]=min(st[i][j],a[x]);
}
il int GetMin_ST(int l,int r){
    re int len=Log(r-l+1);
    return min(st[len][l],st[len][r-(1<<len)+1]);
}
il int GetMin(int l,int r){
    if(id[r]<=id[l]+1){
        re int res=star;
        for(re int i=l;i<=r;i++)if(vis.test(i))res=min(res,a[i]);
        return res;
    }
    return min(min(pre[r],suf[l]),GetMin_ST(id[l]+1,id[r]-1));
}
il void Solve2(){
    memset(st,0x3f,sizeof st);
    memset(pre,0x3f,sizeof pre);
    memset(suf,0x3f,sizeof suf);
    Get_d();
    for(re int p=V;p>=0;p--){
        for(int i:v[p])update(i),vis.set(i);
        for(int x:d[p])
            for(tii i:q2[x])
                ans[get<2>(i)]=min(ans[get<2>(i)],GetMin(get<0>(i),get<1>(i))-p);
                
    }
}
int main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    n=read(),m=read();
    for(re int i=1;i<=n;i++)a[i]=read(),id[i]=(i-1)/len+1,v[a[i]].pb(i);
    for(re int i=1;i<=id[n];i++)L[i]=R[i-1]+1,R[i]=i*len;
    R[id[n]]=n;
    for(re int i=1;i<=m;i++){
        re int l=read(),r=read(),k=read();
        if(k<=T)q1[k].pb(mkt(l,r,i)),s1.set(k);
        else q2[k].pb(mkt(l,r,i)),s2.set(k),ans[i]=star;
    }
    Solve1(),Solve2();
    for(re int i=1;i<=m;i++)cout<<ans[i]<<'\n';
    return 0;
}
posted @   MrcFrst  阅读(41)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示
主题色彩