2023NOIP A层联测32-红楼 ~ Eastern Dream

给出一个长度为 n n n 的序列 a a a,有 m m m 次操作,格式如下:

1 x y k 对于所有满足 ( i − 1 )   m o d   x ≤ y (i-1) \bmod x \le y (i1)modxy i i i,将 a i a_i ai 的值加 k k k

2 l r ∑ i = l r a i \sum\limits_{i=l}^r a_i i=lrai

数组下标从 1 1 1 开始。

n , m ≤ 2 × 1 0 5 , a i , k ≤ 1 0 9 , 1 ≤ x , y ≤ n n,m\le2\times 10^5,a_i,k\le10^9,1\le x,y\le n n,m2×105,ai,k1091x,yn


又是ex数据结构题。考虑根号分治,分成 x ≤ n x\le \sqrt n xn x > n x>\sqrt n x>n 两部分处理。

  • x ≤ n x\le \sqrt n xn 。此时循环节 x x x 很小,可以设 f x , y f_{x,y} fx,y 表示在模 x x x 下,在 y y y 这个位置修改增加的量,同时求出其前缀和 g x , y g_{x,y} gx,y。这个可以 O ( n ) O(\sqrt n) O(n ) 解决。在查询时就直接枚举 x x x,看当前区间 [ l , r ] [l,r] [l,r] 包含多少个完整的 x x x 区间,对于散块就暴力求。
  • x > n x>\sqrt n x>n 。此时连续区间就比较少,线段树可以 O ( n n log ⁡ n ) O(n\sqrt n\log n) O(nn logn),但是会 T,我们希望修改 O ( 1 ) O(1) O(1),查询 O ( n ) O(\sqrt n) O(n ),此时容易想到差分。设差分数组为 c c c,修改是容易的;我们查询的是 ∑ i = l r ∑ j = 1 i c j = ( r − l + 1 ) ∑ i = 1 l − 1 c i + ( r + 1 ) ∑ i = l r c i − ∑ i = l r i ⋅ c i \sum\limits_{i=l}^r\sum\limits_{j=1}^ic_j=(r-l+1)\sum\limits_{i=1}^{l-1}c_i+(r+1)\sum\limits_{i=l}^{r}c_i-\sum\limits_{i=l}^ri\cdot c_i i=lrj=1icj=(rl+1)i=1l1ci+(r+1)i=lrcii=lrici,发现 ∑ c i , ∑ i c i \sum c_i,\sum ic_i ci,ici 都是可以分块维护的,修改 O ( 1 ) O(1) O(1),查询 O ( n ) O(\sqrt n) O(n )

时间复杂度 O ( n n ) O(n\sqrt n) O(nn )。本题卡常,下面代码看看就好。

#include
#define rg register
using namespace std;
#define ll long long
const int N=2e5+1,NN=1000;
int n,m,block,ID[N];
ll g[NN][NN],sum[N];
ll bj1[N],bj2[N],Bj1[N],Bj2[N];
inline void update(int x,int k)
{
    if(x>n) return;
    bj1[x]+=k;
    Bj1[ID[x]]+=k;
    bj2[x]+=1ll*x*k;
    Bj2[ID[x]]+=1ll*x*k;
}
inline ll query1(int l,int r)
{
    ll ans=0;
    int lid=ID[l],rid=ID[r];
    if(lid==rid){
        for(rg int i=l;i<=r;i++) ans+=bj1[i];
        return ans;
    }
    for(rg int i=lid*block+block;i>=l;i--) ans+=bj1[i];
    for(rg int i=lid+1;i<rid;i++) ans+=Bj1[i];
    for(rg int i=rid*block+1;i<=r;i++) ans+=bj1[i];
    return ans; 
}
inline ll query2(int l,int r)
{
    ll ans=0;
    int lid=ID[l],rid=ID[r];
    if(lid==rid){
        for(rg int i=l;i<=r;i++) ans+=bj2[i];
        return ans;
    }
    for(rg int i=lid*block+block;i>=l;i--) ans+=bj2[i];
    for(rg int i=lid+1;i<rid;i++) ans+=Bj2[i];
    for(rg int i=rid*block+1;i<=r;i++) ans+=bj2[i];
    return ans; 
}
int main()
{
    freopen("scarlet.in","r",stdin);
    freopen("scarlet.out","w",stdout);
    cin.tie(0)->sync_with_stdio(0);
    cin>>n>>m;
    block=sqrt(n);
    if(n>=100) block=block*0.55;
    for(rg int i=1,x;i<=n;i++) cin>>x,sum[i]=sum[i-1]+x,ID[i]=(i-1)/block;
    for(rg int t=1,op,x,y,k,l,r;t<=m;t++){
        cin>>op;
        if(op==1){
            cin>>x>>y>>k;
            y=min(x-1,y);
            if(x<=block){
                ll K=k;
                for(rg int i=0;i<=y;i++,K+=k) g[x][i]+=K;
                K-=k;
                for(rg int i=y+1;i<x;i++) g[x][i]+=K;
            }
            else{
                for(rg int i=1;i<=n;i+=x){
                    update(i,k);
                    update(i+y+1,-k);
                }
            }
        }
        else{
            cin>>l>>r;
            ll ans=sum[r]-sum[l-1];
            for(rg int i=1;i<=block;i++){
                int lid=(l-1)/i,rid=(r-1)/i,L=(l-1)-lid*i,R=(r-1)-rid*i;
                ans+=(rid-lid)*g[i][i-1]+g[i][R]-(L?g[i][L-1]:0);
            }
            cout<<ans+(r-l+1)*query1(1,l-1)+query1(l,r)*(r+1)-query2(l,r)<<"\n";
        }
    }
}

你可能感兴趣的:(算法)