RMQ

区间最大最小值(RMQ)

st 表

利用 min,max区间合并是可重的,倍增预处理

时间复杂度 \(O(n \log n+ q)\)

空间复杂度 \(O(n\log n)\)

线段树

二进制划分区间

时间复杂度 \(O(n \log n)\)

method of four russians

建立笛卡尔树,求欧拉序,序列中相邻的两个元素绝对值为 1,分块RMQ,状压,然后可以 \(O(n)-O(1)\)

然而位运算需要一定复杂度,有常数,跑起来甚至比st表慢。

看看就行。

// P3793 lxl 毒瘤题
#include<bits/stdc++.h>
using namespace std;
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=20000001;
int n,m,s;
int a[N];
struct Node{
    int ls,rs;
    #define lc(p) t[p].ls
    #define rc(p) t[p].rs
}t[N];
int rt;
const int M=3076924+10;
int dfc,B,cntB,euler[N<<1],dep[N<<1],lg[N<<1];
int block[N<<1],L[M],R[M],dif[M],f[M][23],g[M][23];
int idx[N],c[1<<15];
int stk[N],tp;
void build_Cartesian(){
    stk[tp=1]=0;
    a[0] = 2e9;
    for(int i=1;i<=n;++i){
        while(a[stk[tp]]<a[i])tp--;
        int pos=stk[tp];
        lc(i) = rc(pos);
        rc(pos) = i;
        stk[++tp]=i;
    }
    rt=stk[2];
}
void get_Euler(int x,int Dep){
    euler[++dfc]=x;
    dep[dfc]=Dep;
    idx[x]=dfc;
    if(lc(x)){
        get_Euler(lc(x),Dep+1);
        euler[++dfc]=x;
        dep[dfc]=Dep;
    }
    if(rc(x)){
        get_Euler(rc(x),Dep+1);
        euler[++dfc]=x;
        dep[dfc]=Dep;
    }
}
void divide(){
    for(int i=1;i<=cntB;++i){
        L[i]=(i-1)*B+1;
        R[i]=min(i*B,dfc);
        for(int j=L[i];j<=R[i];++j)block[j]=i;
    }
}
void small_init(){
    for(int i=1;i<=cntB;i++){
        for(int j=L[i];j<=R[i];++j)
            if(j>L[i]&&dep[j-1]>dep[j])
                dif[i]|=(1<<j-L[i]-1);
    }
    for(int i=0;i<(1<<B-1);++i){
        int sum=0,mn=0;
        c[i]=0;
        for(int j=1;j<B;++j){
            sum+=(i>>(j-1)&1)?-1:1;
            if(mn>sum)mn=sum,c[i]=j;
        }
    }
}
void block_init(){
    for(int i=1;i<=cntB;i++){
        f[i][0]=2e9;
        for(int j=L[i];j<=R[i];++j){
            if(dep[j]<f[i][0]){
                f[i][0]=dep[j];
                g[i][0]=j;
            }
        }
    }
    for(int j=1;j<=lg[cntB];++j){
        for(int i=1,I=cntB-(1<<j)+1;i<=I;++i){
            int t=i+(1<<(j-1));
			if(f[i][j-1]<f[t][j-1]){
                f[i][j]=f[i][j-1];
                g[i][j]=g[i][j-1]; 
            }else{
                f[i][j]=f[t][j-1];
                g[i][j]=g[t][j-1];
            }
        }
    }
}
int small_query(int l,int r){
    int p=block[l],S=(dif[p]>>(l-L[p]))&((1<<r-l)-1);
    return c[S]+l;
}
int block_query(int l,int r){
    int k=log2(r-l+1);
    return f[l][k]<=f[r-(1<<k)+1][k]?g[l][k]:g[r-(1<<k)+1][k];
}
int query(int l,int r){
    if(l>r)swap(l,r);//确实有序数对,在欧拉序上不一定有序,因为欧拉序不满足二叉搜索树遍历
    if(block[l]==block[r])return small_query(l,r);
    else{
        int res1=small_query(l,R[block[l]]),res2=small_query(L[block[r]],r);
        int ans=(dep[res1]<dep[res2]?res1:res2);
        if(block[l]+1<=block[r]-1){
            int res3=block_query(block[l]+1,block[r]-1);
            if(dep[res3]<dep[ans])ans=res3;
        }
        return ans;
    }
}
void Read(int &a){
    #define gc getchar()
    char c=gc;a=0;
    while(c<'0'||c>'9')c=gc;
    while(c>='0'&&c<='9')a=a*10+c-'0',c=gc;
}
int main(){
//    freopen("in.in","r",stdin);
//    freopen("o.out","w",stdout);
    Read(n);Read(m);Read(s);
    srand(s);
    for(int i=1;i<=n;++i)a[i]=read();
    lg[0]=-1;
    for(int i=1;i<=n<<1;++i)lg[i]=lg[i>>1]+1;
    build_Cartesian();
    get_Euler(rt,1);
    B=ceil(log2(dfc)/2+0.1),cntB=(dfc-1)/B+1;
    divide();
    small_init();
    block_init();
    unsigned long long ans=0;
    for(int i=1;i<=m;++i){
        int l=read()%n+1,r=read()%n+1;
        if(l>r)swap(l,r);
        int tmp=query(idx[l],idx[r]);
        ans += a[euler[tmp]];
    }
    printf("%llu\n",ans);
    cerr<<(double)clock()/CLOCKS_PER_SEC<<"s"<<endl;
    return 0;
}
posted @ 2024-02-22 23:39  妖灵梦  阅读(33)  评论(0)    收藏  举报