(多校) 古老的序列问题

考虑分治
将原序列拆成若干新区间,求经过 \(mid\) 的询问区间
在一段处理区间内,另开 \(4\) 棵线段树分别维护四段(根据最大值最小值划分)的系数
合并询问区间即可
注意如果某一个询问区间恰好包含该分治区间
直接后续更新答案即可,不然空间就爆成 \(n^2\) 了
这道题主要学的分治思想

Code


#include <bits/stdc++.h>
#define re register
#define db double
#define int long long
// #define ll long long
#define pir make_pair
using namespace std;
const int N=2e6+10;
const int maxn=4E5+10;
const int INF=1e18+10;
const int mol=1E9+7;
inline int read(){
    int x=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9') w=(ch=='-')?-1:1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*w;
}

int n,m,a[maxn],ansl[maxn],maxl[maxn],minl[maxn],f[maxn];
struct QUS { int l,r,id; };
vector<QUS>qus[maxn<<2];
#define lid (id<<1)
#define rid (id<<1|1)
inline void ad(int &x) { x= x>=mol? x-mol:x; }
inline int ad1(int x) { return x>=mol? x-mol:x; }
struct STG {
    int num[maxn];
    struct TREE { int lazy,sum,ls; } tre[maxn<<2];
    inline void init(int id,int l,int r) {
        tre[id].lazy=tre[id].sum=0;
        if(l==r) { tre[id].ls=num[l]; return ; }
        int mid=(l+r)>>1;
        init(lid,l,mid); init(rid,mid+1,r);
        ad(tre[id].ls=tre[lid].ls+tre[rid].ls);
    }
    inline void push_down(int id) {  
        int s=tre[id].lazy; tre[id].lazy=0;
        ad(tre[lid].sum+=tre[lid].ls*s%mol); ad(tre[rid].sum+=tre[rid].ls*s%mol);
        ad(tre[lid].lazy+=s); ad(tre[rid].lazy+=s);
    }
    inline void insert(int id,int l,int r,int ll,int rr,int val) {
        if(ll<=l&&r<=rr) { ad(tre[id].sum+=tre[id].ls*val%mol); ad(tre[id].lazy+=val); return ; }
        if(tre[id].lazy) push_down(id);
        int mid=(l+r)>>1;
        if(ll<=mid) insert(lid,l,mid,ll,rr,val); if(rr>mid) insert(rid,mid+1,r,ll,rr,val);
        ad(tre[id].sum=tre[lid].sum+tre[rid].sum);
    }
    inline int query(int id,int l,int r,int ll,int rr) {
        if(ll<=l&&r<=rr) return tre[id].sum;
        if(tre[id].lazy) push_down(id);
        int mid=(l+r)>>1,ans=0;
        if(ll<=mid) ad(ans+=query(lid,l,mid,ll,rr)); if(rr>mid) ad(ans+=query(rid,mid+1,r,ll,rr));
        return ans;
    }
} tree[4];
inline void slove(int id,int l,int r) {
    if(l==r) {
        f[id]=a[l]*a[l]%mol;
        for(re int i=0;i<qus[id].size();i++) ad(ansl[qus[id][i].id]+=a[l]*a[l]%mol);
        return ;
    }
    vector<int>vec;
    int mid=(l+r)>>1;
    sort(qus[id].begin(),qus[id].end(),[](QUS a,QUS b){ return a.r<b.r; });
    int fg=0;
    while(fg<qus[id].size()&&qus[id][fg].r<mid+1) ++fg;
    maxl[mid]=a[mid]; minl[mid]=a[mid];
    for(re int i=mid-1;i>=l;i--) {
        maxl[i]=max(a[i],maxl[i+1]); minl[i]=min(a[i],minl[i+1]);
    }
    for(re int i=l;i<=mid;i++) {
        tree[0].num[i]=1; tree[1].num[i]=maxl[i]; tree[2].num[i]=minl[i]; tree[3].num[i]=maxl[i]*minl[i]%mol;
    }
    for(re int i=0;i<4;i++) tree[i].init(1,l,mid);
    int mi=INF,mx=-INF,x=mid+1,y=mid+1,z=mid+1;
    for(re int i=mid+1;i<=r;i++) {
        mi=min(mi,a[i]); mx=max(mx,a[i]);
        while(x>l&&minl[x-1]>=mi&&maxl[x-1]<=mx) --x;
        while(y>l&&minl[y-1]>=mi) --y;
        while(z>l&&maxl[z-1]<=mx) --z;
        int ls=0;
        if(x<=mid) tree[0].insert(1,l,mid,x,mid,mi*mx%mol);
        if(y<x) {
            tree[1].insert(1,l,mid,y,x-1,mi);
            if(y>l) tree[3].insert(1,l,mid,l,y-1,1);
        }
        if(z<x) {
            tree[2].insert(1,l,mid,z,x-1,mx);
            if(z>l) tree[3].insert(1,l,mid,l,z-1,1);
        }
        while(fg<qus[id].size()&&qus[id][fg].r==i) {
            if(qus[id][fg].l<l||qus[id][fg].l>mid||(qus[id][fg].l==l&&qus[id][fg].r==r)) { ++fg; continue; }
            for(re int j=0;j<4;j++) ad(ansl[qus[id][fg].id]+=tree[j].query(1,l,mid,qus[id][fg].l,mid));
            ++fg;
        }
    }
    for(re int i=0,ll,rr;i<qus[id].size();i++) {
        ll=qus[id][i].l; rr=qus[id][i].r;
        if(ll==l&&rr==r) { vec.push_back(qus[id][i].id); continue; }
        if(ll>mid) qus[rid].push_back(qus[id][i]);
        else if(rr<=mid) qus[lid].push_back(qus[id][i]);
        else {
            qus[lid].push_back((QUS){ ll,mid,qus[id][i].id });
            qus[rid].push_back((QUS){ mid+1,rr,qus[id][i].id });
        }
    }
    for(re int i=0;i<4;i++) ad(f[id]+=tree[i].tre[1].sum);
    slove(lid,l,mid); slove(rid,mid+1,r);
    ad(f[id]+=ad1(f[lid]+f[rid]));
    for(re int i=0;i<vec.size();i++) {
        int ls=vec[i];
        ad(ansl[ls]+=f[id]);
    }
}
signed main(void) {
    // freopen("erp.in","r",stdin); freopen("erp.out","w",stdout);
    freopen("sequence.in","r",stdin); freopen("sequence.out","w",stdout); 
    n=read(),m=read();
    for(re int i=1;i<=n;i++) a[i]=read();
    for(re int i=1,l,r;i<=m;i++) {
        l=read(),r=read(); qus[1].push_back((QUS){ l,r,i });
    }
    slove(1,1,n);
    for(re int i=1;i<=m;i++) printf("%lld\n",ansl[i]);
    return 0;
}

上一篇:单链表LRU


下一篇:微信运动圈自动点赞(自动跳过自己)