天天看点

舒老师AK的hu测 T2. LX还在迷路(线段树+等差数列)

版权属于舒老师,想要引用此题(包括题面)的朋友请联系博主

舒老师AK的hu测 T2. LX还在迷路(线段树+等差数列)
舒老师AK的hu测 T2. LX还在迷路(线段树+等差数列)
舒老师AK的hu测 T2. LX还在迷路(线段树+等差数列)

分析:

这道题是舒老师的原创题(版权声明~)

神题啊,毒瘤题啊

正解

首先,我们可以先忽略 n(n+1)2 n ( n + 1 ) 2 中的 2 2 ,最后再处理即可

我们将 n(n+1) n ( n + 1 ) 展开,得到 n2+n n 2 + n

我们可以把一个操作拆成两个:

① 对 [l,r] [ l , r ] 每个点加 dis2 d i s 2 ( dis d i s 为该点到l的距离+1)

② 对 [l,r] [ l , r ] 每个点加 dis d i s

(操作②这就让我想起一道题,相当于加上一条直线)

我们从简单的开始

操作②

在区间 [l,r] [ l , r ] 上加上一个等差数列: a1=1,d=1 a 1 = 1 , d = 1

在线段树众维护等差数列的首项和公差

在pushdown的时候,左儿子直接继承父结点的首项和公差,右儿子继承公差,计算一下首项即可

如果打标记时发生了标记覆盖,直接:首项1+首项2,公差1+公差2

操作①

显然我们没法合并 x2 x 2 的标记,考虑差分

n2−(n−1)2=2n−1 n 2 − ( n − 1 ) 2 = 2 n − 1

等差数列: a1=1,d=2 a 1 = 1 , d = 2 ,在线段树中维护首项和公差

如果是单点查询的话,我们查询前缀和即可(差分的运用)

如果是区间 [l,r],ans=∑ri=li2 [ l , r ] , a n s = ∑ i = l r i 2

设 a[i]=2i−1 a [ i ] = 2 i − 1

舒老师AK的hu测 T2. LX还在迷路(线段树+等差数列)

由上图(等高线地形图???)就可以看出:

a[1]−a[l] a [ 1 ] − a [ l ] 计算了 r−l+1 r − l + 1 次

a[l+1] a [ l + 1 ] 计算了 r−l r − l 次

... . . .

a[r] a [ r ] 计算了 1 1 次

我们定义C(l,r)C(l,r)

C(l,r)= C ( l , r ) =

a[l]+a[l+1]+a[l+2]+...a[r]+ a [ l ] + a [ l + 1 ] + a [ l + 2 ] + . . . a [ r ] +

a[l+1]+a[l+2]+...a[r]+ a [ l + 1 ] + a [ l + 2 ] + . . . a [ r ] +

a[l+2]+...a[r]+ a [ l + 2 ] + . . . a [ r ] +

... . . .

a[r] a [ r ]

那么 ans=∑ri=li2=∑l−1i=1a[i]∗(r−l+1)+C(l,r) a n s = ∑ i = l r i 2 = ∑ i = 1 l − 1 a [ i ] ∗ ( r − l + 1 ) + C ( l , r )

维护 C(l,r) C ( l , r )

update: C(l,r)=C(l,mid)+C(mid+1,r)+∑midi=la[i]∗(r−mid) C ( l , r ) = C ( l , m i d ) + C ( m i d + 1 , r ) + ∑ i = l m i d a [ i ] ∗ ( r − m i d )

方便起见,我们可以标记永久化

#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long

using namespace std;

const int N=;
const ll p=e9+;
ll inv2,s1[N],s2[N];
int n,type,L,R,m,lastans=;

ll KSM(ll a,ll b) {
    ll t=;
    while (b) {
        if (b&) t=(t*a)%p;
        a=(a*a)%p;
        b>>=;
    }
    return t%p;
}

void change_lr() {
    L=(L+lastans-)%n+;
    R=(R+lastans-)%n+;
    if (L>R) swap(L,R);
}

struct node1{                   //维护等差数列  
    struct tree1{
        ll a,b,sum;
    }t[N<<];

    void update(int bh,int l,int r) {     
        t[bh].sum=t[bh<<].sum+t[bh<<|].sum; t[bh].sum%=p;
        if (!t[bh].a&&!t[bh].b) return;    
        ll n=r-l+;                                                //有标记就维护一下值 
        t[bh].sum+=(t[bh].a*n%p+t[bh].b*s1[n]%p)%p; t[bh].sum%=p;
    }

    void change(int bh,int l,int r,int L,int R,int a,int b) {
        if (l>=L&&r<=R) {
            t[bh].a+=a; t[bh].a%=p;
            t[bh].b+=b; t[bh].b%=p;
            update(bh,l,r);
            return;
        }
        int mid=(l+r)>>;
        if (L<=mid) change(bh<<,l,mid,L,R,a,b);
        if (R>mid) change(bh<<|,mid+,r,L,R,(a+max(mid-max(l,L)+,)*b%p)%p,b);
        update(bh,l,r);
    }

    ll ask(int bh,int l,int r,int L,int R,ll a,ll b) {    //标记永久化的a和b 
        if (l>=L&&r<=R) {
            return (t[bh].sum+a*(r-l+)%p+b*(s1[r-l+])%p)%p;
        }
        int mid=(l+r)>>;
        a=(a+t[bh].a)%p; b=(b+t[bh].b)%p;
        //标记永久化 
        ll ans=;
        if (L<=mid) ans=(ans+ask(bh<<,l,mid,L,R,a,b)%p)%p;
        if (R>mid) ans=(ans+ask(bh<<|,mid+,r,L,R,(a+(mid-l+)*b%p)%p,b)%p)%p;
        //a+(mid-l+)*b  右儿子新首项 
        return ans;
    }
};
node1 T1;

struct node2{
    struct tree2{
        ll a,b,sum,sum_2;
        //sum sum(a[l]~a[r])
        //sum_2 C(l,r) 
    }t[N<<];

    void update(int bh,int l,int r) {
        int mid=(l+r)>>;
        int lc=bh<<;
        int rc=bh<<|;
        t[bh].sum=t[lc].sum+t[rc].sum; t[bh].sum%=p;
        t[bh].sum_2=t[lc].sum_2+t[rc].sum_2; t[bh].sum_2%=p;      
        t[bh].sum_2+=t[lc].sum*(ll)(r-mid)%p;  t[bh].sum_2%=p; 
        if (!t[bh].a&&!t[bh].b) return;                          //有标记就维护一下值
        ll n=r-l+;
        t[bh].sum+=(t[bh].a*n%p+t[bh].b*s1[n]%p)%p; t[bh].sum%=p;
        t[bh].sum_2+=t[bh].a*s1[n]%p+t[bh].b*s1[n]%p*(n+)%p-s2[n]*t[bh].b%p;
        //注意sum_2的维护 
        t[bh].sum_2%=p; 
    }

    void change(int bh,int l,int r,int L,int R,ll a,ll b) {
        if (l>=L&&r<=R) {
            t[bh].a+=a; t[bh].a%=p;
            t[bh].b+=b; t[bh].b%=p;
            update(bh,l,r);
            return;
        }
        int mid=(l+r)>>;
        if (L<=mid) change(bh<<,l,mid,L,R,a,b);
        if (R>mid) change(bh<<|,mid+,r,L,R,(a+max(mid-max(l,L)+,)*b%p)%p,b);
        update(bh,l,r);
    }

    ll ask(int bh,int l,int r,int L,int R,ll s,ll a,ll b) {
        if (l>=L&&r<=R) {
            ll n=r-l+;
            return (s*n%p+t[bh].sum_2+a*s1[n]%p+b*s1[n]%p*(n+)%p-b*s2[n]%p);
        }
        int mid=(l+r)>>;
        a=(a+t[bh].a)%p; b=(b+t[bh].b)%p;
        //标记永久化 
        ll ans=;
        if (L<=mid) ans=(ans+ask(bh<<,l,mid,L,R,s,a,b))%p;
        if (R>mid) ans=(ans+ask(bh<<|,mid+,r,L,R,(s+t[bh<<].sum+a*(mid-l+)%p+b*s1[mid-l+]%p)%p,(a+b*(mid-l+)%p)%p,b)%p)%p;
        return ans%p;
    }
};
node2 T2;

int main() {
    char s[];
    scanf("%d%d",&n,&type);
    scanf("%d",&m);

    inv2=KSM(,p-);
    for (int i=;i<=n;i++) {
        s1[i]=(s1[i-]+(ll)i)%p;
        s2[i]=(s2[i-]+(ll)i*i)%p;
    }

    for (int i=;i<=m;i++) {
        scanf("%s%d%d",s,&L,&R);
        if (type) change_lr();
        if (s[]=='C') {
            T1.change(,,n,L,R,,);      //a= b=
            T2.change(,,n,L,R,-,);     //a=- b=
            if (R!=n) {
                ll nn=R-L+;
                T2.change(,,n,R+,R+,-nn*nn%p,);
            }
        }
        else {
            ll ans=(T1.ask(,,n,L,R,,)+T2.ask(,,n,L,R,,,))%p;
            ans=(ans*inv2%p+p)%p;
            lastans=ans%p;
            printf("%lld\n",ans);    
        }
    }
    return ;
}
           

Enzymii解

预处理 mul[j]=∑ji=1i∗(i+1)2 m u l [ j ] = ∑ i = 1 j i ∗ ( i + 1 ) 2

分块(有点像块状链表)

code看不大懂,总的来说也是维护等差数列的首项和公差

zyz解

首先我们观察一下 i(i+1)2 i ( i + 1 ) 2 ,并且差分一下

有兴趣的可以看一下差分序列的相关资料

1   3   6   10  15 
  2   3   4   5
    1   1   1
           

可以发现, i(i+1)2 i ( i + 1 ) 2 是一个二阶等差数列

我们用线段树维护一阶首项,二阶首项,二阶公差

如果打标记时发生了标记覆盖,直接对应相加即可

看一下如何计算序列中的第 pos p o s 项:

a1+b1∗pos+pos(pos−1)2∗d a 1 + b 1 ∗ p o s + p o s ( p o s − 1 ) 2 ∗ d