一类双排列区间操作问题

基础版本

给出一个长度为 \(n\) 的排列 \(p_i\),进行 \(m\) 次区间操作,要么 \(\forall l\leq i\leq r, a_i\leftarrow a_i+w\),要么求 \(\sum_{i=l}^{r}a_{p_i}\)

做法

分块,设块长为 \(B\)

散块对散块和整块对散块是简单的,复杂度为 \(O(m\times \frac{n}{B}+mB)\)

整块对整块,可以预处理 \(p_i\) 的前缀整块在 \(id_i\) 的前缀整块的出现次数,差分即可,复杂度为 \(O((\frac{n}{B})^2\times B+m\times \frac{n}{B})\)

散块对整块,暴力找逆位置所在的块,求前缀和后更新,复杂度为 \(O(mB+m\times \frac{n}{B})\)

\(B=\sqrt{n}\) 平衡得到时间复杂度为 \(O((n+m)\sqrt{n})\)

空间复杂度在于维护两个前缀,为 \(O(B^2)=O(n)\)

2024-2025 集训队互测 Round 2 (Oct 19, 2024) B

树剖后就是基础版本,时间复杂度为 \(O((n+m)\sqrt{n}\log n)\),空间复杂度为 \(O(n)\)

存在时间复杂度为 \(O((n+m)\sqrt n)\) 的做法。

代码
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int N=2e5+5;
const int B=450;
// const int B=2;
int n, m;
struct DS{
    int p[N], revp[N];
    int pos[N], lp[B], rp[B];
    int cnt[B][B];
    vector<int> occ[N];
    ull atag[B], aself[N];
    ull sum[B], dt[B];
    inline ull qryp(int x){
        return atag[pos[x]]+aself[x];
    }
    inline ull qry(int x){
        if(x==0) return 0;
        ull ret=sum[pos[x]-1];
        for(int i=lp[pos[x]]; i<=x; ++i){
            ret+=qryp(p[i]);
        }
        return ret;
    }
    void init(){
        for(int i=1; i<=n; ++i) pos[i]=(i-1)/B+1;
        for(int i=1; i<=pos[n]; ++i) lp[i]=rp[i-1]+1, rp[i]=rp[i-1]+B;
        rp[pos[n]]=n;
        for(int i=1; i<=pos[n]; ++i){
            for(int j=1; j<=pos[n]; ++j){
                cnt[i][j]=cnt[i][j-1];
                for(int k=lp[i]; k<=rp[i]; ++k){
                    cnt[i][j]+=(p[k]>=lp[j]&&p[k]<=rp[j]);
                }
            }
        }
        for(int i=1; i<=pos[n]; ++i){
            for(int j=1; j<=pos[n]; ++j){
                cnt[i][j]+=cnt[i-1][j];
            }
        }
    }
    void mdf(int l, int r, ull x){
        if(pos[l]==pos[r]){
            for(int i=l; i<=r; ++i) aself[i]+=x, dt[pos[revp[i]]]++;
        }
        else{
            for(int i=l; i<=rp[pos[l]]; ++i) aself[i]+=x, dt[pos[revp[i]]]++;
            for(int i=lp[pos[r]]; i<=r; ++i) aself[i]+=x, dt[pos[revp[i]]]++;
            for(int i=pos[l]+1; i<pos[r]; ++i) atag[i]+=x;
            for(int i=1; i<=pos[n]; ++i) {
                sum[i]+=x*(cnt[i][pos[r]-1]-cnt[i][pos[l]]);
            }
        }
    }
}D;
ull a[N];
struct Tree{
    vector<int> e[N];
    int sz[N], son[N], dfn[N], timer, top[N], f[N], dep[N];
    void dfs1(int x, int fa){
        f[x]=fa; sz[x]=1; dep[x]=dep[fa]+1;
        for(auto y:e[x]) if(y^fa) {
            dfs1(y, x);
            sz[x]+=sz[y];
            if(sz[y]>sz[son[x]]) son[x]=y;
        }
    }
    void dfs2(int x, int tp){
        top[x]=tp; dfn[x]=++timer;
        if(son[x]) dfs2(son[x], tp);
        for(auto y:e[x]) if(y!=f[x]&&y!=son[x]){
            dfs2(y, y);
        }
    }
    vector<pair<int, int> > split(int x, int y){
        vector<pair<int, int> > ret;
        while(top[x]!=top[y]){
            if(dep[top[x]]<dep[top[y]]) swap(x, y);
            ret.push_back(make_pair(dfn[top[x]], dfn[x]));
            x=f[top[x]];
        }
        if(dfn[x]>dfn[y]) swap(x, y);
        ret.push_back(make_pair(dfn[x], dfn[y]));
        return ret;
    }
}t1, t2;
int main(){
	// freopen("D:\\nya\\acm\\A\\test.in","r",stdin);
	// freopen("D:\\nya\\acm\\A\\test.out","w",stdout);
    scanf("%d%d", &n, &m);
    for(int i=1; i<=n; ++i) scanf("%llu", &a[i]);
    for(int i=1, x, y; i<n; ++i){
        scanf("%d%d", &x, &y);
        t1.e[x].push_back(y);
        t1.e[y].push_back(x);
    }
    for(int i=1, x, y; i<n; ++i){
        scanf("%d%d", &x, &y);
        t2.e[x].push_back(y);
        t2.e[y].push_back(x);
    }
    t1.dfs1(1, 0); t1.dfs2(1, 1);
    t2.dfs1(1, 0); t2.dfs2(1, 1);
    for(int i=1; i<=n; ++i){
        D.p[t2.dfn[i]]=t1.dfn[i];
        D.revp[t1.dfn[i]]=t2.dfn[i];
    }
    D.init();
    for(int i=1; i<=n; ++i){
        D.sum[D.pos[t2.dfn[i]]]+=a[i];
        D.aself[t1.dfn[i]]=a[i];
    }
    for(int i=1; i<=D.pos[n]; ++i) D.sum[i]+=D.sum[i-1];
    int l, r; ull x;
    while(m--){
        scanf("%d%d%llu", &l, &r, &x);
        vector<pair<int, int> > v1=t1.split(l, r);
        for(auto t:v1){
            D.mdf(t.first, t.second, x);
        }
        for(int i=1; i<=D.pos[n]; ++i) {
            D.dt[i+1]+=D.dt[i];
            D.sum[i]+=x*D.dt[i];
            D.dt[i]=0;
        }
        ull ans=0;
        vector<pair<int, int> > v2=t2.split(l, r);
        for(auto t:v2){
            ans+=D.qry(t.second)-D.qry(t.first-1);
        }
        printf("%llu\n", ans);
    }
}

2025牛客暑期多校训练营6 E

给出的不是排列,但实际上可以改造成排列。

具体的,删除没出现的数,把每个数复制(出现次数)次,就变成了基础版本。

时间复杂度为 \(O((n+m)\sqrt{n})\),空间复杂度为 \(O(n)\)

代码
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
const int B=350;
// const int B=2;
int n, m;
int p[N], revp[N];
int cl[N], cr[N];
int pos[N], lp[350], rp[350];
int cnt[350][350];
vector<int> occ[N];
typedef long long ll;
ll atag[350], aself[N];
ll sum[350], dt[350];
inline ll qryp(int x){
    return atag[pos[x]]+aself[x];
}
inline ll qry(int x){
    if(x==0) return 0;
    ll ret=sum[pos[x]-1];
    for(int i=lp[pos[x]]; i<=x; ++i){
        ret+=qryp(p[i]);
    }
    return ret;
}
int main(){
	// freopen("D:\\nya\\acm\\A\\test.in","r",stdin);
	// freopen("D:\\nya\\acm\\A\\test.out","w",stdout);
    scanf("%d%d", &n, &m);
    for(int i=1; i<=n; ++i) scanf("%d", &p[i]), occ[p[i]].push_back(i);
    for(int i=1; i<=n; ++i){
        cl[i]=cr[i-1]+1;
        cr[i]=cr[i-1];
        for(auto t:occ[i]){
            ++cr[i];
            p[t]=cr[i];
            revp[cr[i]]=t;
        }
    }
    for(int i=1; i<=n; ++i) pos[i]=(i-1)/B+1;
    for(int i=1; i<=pos[n]; ++i) lp[i]=rp[i-1]+1, rp[i]=rp[i-1]+B;
    rp[pos[n]]=n;
    for(int i=1; i<=pos[n]; ++i){
        for(int j=1; j<=pos[n]; ++j){
            cnt[i][j]=cnt[i][j-1];
            for(int k=lp[i]; k<=rp[i]; ++k){
                cnt[i][j]+=(p[k]>=lp[j]&&p[k]<=rp[j]);
            }
        }
    }
    for(int i=1; i<=pos[n]; ++i){
        for(int j=1; j<=pos[n]; ++j){
            cnt[i][j]+=cnt[i-1][j];
        }
    }
    ll lstans=0;
    int tp; ll l, r, x;
    while(m--){
        scanf("%d%lld%lld", &tp, &l, &r);
        l^=lstans; r^=lstans;
        if(tp==1){
            scanf("%lld", &x);
            x^=lstans;
            l=cl[l]; r=cr[r];
            if(l>r) continue;
            if(pos[l]==pos[r]){
                for(int i=l; i<=r; ++i) aself[i]+=x, dt[pos[revp[i]]]++;
            }
            else{
                for(int i=l; i<=rp[pos[l]]; ++i) aself[i]+=x, dt[pos[revp[i]]]++;
                for(int i=lp[pos[r]]; i<=r; ++i) aself[i]+=x, dt[pos[revp[i]]]++;
                for(int i=pos[l]+1; i<pos[r]; ++i) atag[i]+=x;
                for(int i=1; i<=pos[n]; ++i) {
                    sum[i]+=x*(cnt[i][pos[r]-1]-cnt[i][pos[l]]);
                }
            }
            for(int i=1; i<=pos[n]; ++i) {
                dt[i+1]+=dt[i];
                sum[i]+=x*dt[i];
                dt[i]=0;
            }
        }
        else{
            lstans=qry(r)-qry(l-1);
            printf("%lld\n", lstans);
        }
    }
}
posted @ 2025-07-31 23:50  Displace  阅读(9)  评论(0)    收藏  举报