为什么询问这么慢

P3793 由乃救爷爷

hh弟中弟 @ 2024-12-22 15:30:27

RT,写的分块 st 表,块长 32,T 了两个点,发现是询问 T 了,预处理跑了不到一秒。

#include<bits/stdc++.h>
#define fi first
#define se second
#define pii std::pair<int,int>
#define eb emplace_back
#define pb push_back
typedef long long ll;
typedef unsigned long long ull;
std::mt19937_64 myrand(std::chrono::high_resolution_clock::now().time_since_epoch().count());
inline int R(int n){return myrand()%n+1;}
namespace GenHelper
{
    unsigned z1,z2,z3,z4,b;
    unsigned rand_()
    {
    b=((z1<<6)^z1)>>13;
    z1=((z1&4294967294U)<<18)^b;
    b=((z2<<2)^z2)>>27;
    z2=((z2&4294967288U)<<2)^b;
    b=((z3<<13)^z3)>>21;
    z3=((z3&4294967280U)<<7)^b;
    b=((z4<<3)^z4)>>12;
    z4=((z4&4294967168U)<<13)^b;
    return (z1^z2^z3^z4);
    }
}
void srand(unsigned x)
{using namespace GenHelper;
z1=x; z2=(~x)^0x233333333U; z3=x^0x1234598766U; z4=(~x)+51;}
int read()
{
    using namespace GenHelper;
    int a=rand_()&32767;
    int b=rand_()&32767;
    return a*32768+b;
}
const int N=2e7+5,inf=1e9,len=32;
inline void Min(int &x,int y){if(x>y)x=y;}
inline void Max(int &x,int y){if(x<y)x=y;}
int n,m,id[N],a[N],St[20][N/len+1],num;
short st[6][N];
ull ans;
inline int fl(int id){return (id-1)*len+1;}
inline int fr(int id){return std::min(n,fl(id)+len-1);}
inline int get(int id,int x,int y){int L=fl(id);return a[L+x]>a[L+y]?x:y;}
inline int getsan(int id,int l,int r){
    if(l>r)return 0;
    int L=fl(id);int d=std::__lg(r-l+1);
    return a[L+get(id,st[d][l],st[d][r-(1<<d)+1])];
}
inline int getz(int l,int r){
    if(l>r)return 0;int d=std::__lg(r-l+1);
    return std::max(St[d][l],St[d][r-(1<<d)+1]);
}
signed main(){
    // freopen("in.in","r",stdin);freopen("out.out","w",stdout);
    std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
    int zc;std::cin>>n>>m>>zc;srand(zc);num=(n-1)/len+1;
    for(int i=1;i<=n;++i){
        a[i]=read();id[i]=(i-1)/len+1;Max(St[0][id[i]],a[i]);
    }
    for(int i=1;i<=num;++i){
        int l=fl(i),r=fr(i),id=i;
        for(int i=l;i<=r;++i)st[0][i]=i-l;
        for(int i=1;i<=5;++i)for(int j=l;j+(1<<i)-1<=r;++j)
            st[i][j]=get(id,st[i-1][j],st[i-1][j+(1<<i-1)]);
    }
    for(int i=1;i<=std::__lg(num);++i)for(int j=1;j+(1<<i)-1<=num;++j)
        St[i][j]=std::max(St[i-1][j],St[i-1][j+(1<<i-1)]);
    for(int i=1;i<=m;++i){
        int l=read()%n+1,r=read()%n+1;if(l>r)std::swap(l,r);
        int lid=id[l],rid=id[r],R=fr(lid),L=fl(rid);
        if(lid==rid){ans+=(getsan(lid,l,r));continue;}
        ans+=std::max(std::max(getsan(lid,l,R),getsan(rid,L,r)),getz(lid+1,rid-1));
    }std::cout<<ans<<'\n';
}

by hh弟中弟 @ 2024-12-22 15:31:28

这一部分 T 了。

for(int i=1;i<=m;++i){
  int l=read()%n+1,r=read()%n+1;if(l>r)std::swap(l,r);
  int lid=id[l],rid=id[r],R=fr(lid),L=fl(rid);
  if(lid==rid){ans+=(getsan(lid,l,r));continue;}
  ans+=std::max(std::max(getsan(lid,l,R),getsan(rid,L,r)),getz(lid+1,rid-1));
}std::cout<<ans<<'\n';

by wangziyue_AK @ 2024-12-22 15:36:09

查询函数里的

std::__lg(r-l+1)

太慢了,建议预处理log


by wangziyue_AK @ 2024-12-22 15:37:20

@hh弟中弟


by hh弟中弟 @ 2024-12-22 15:38:51

@wangziyue_AK

额,预处理之后的也 T


by wangziyue_AK @ 2024-12-22 15:44:42

@hh弟中弟 建议少一些函数调用,块的左右端点都能预处理


by hh弟中弟 @ 2024-12-22 15:45:38

@wangziyue_AK

改成前后缀预处理后用 C++98 过了。


by 5k_sync_closer @ 2024-12-22 15:46:32

@wangziyue_AK lg 是用 builtin_clz 写的,常数很小


by wangziyue_AK @ 2024-12-22 15:48:31

@5k_sync_closer哦,那我之前听了一些误人子弟的讲解,自己也开始犯傻了,谢谢


|