【LuoguP4927】夢美的線段樹(LGR-053)-線段樹

測試地址:夢美的線段樹
做法: 本題需要用到線段樹。
經過簡單的計算,題目所求的是:
ans=sumi2sum1ans=\frac{\sum sum_i^2}{sum_1}
於是我們現在要求的就是sumi2\sum sum_i^2
向上合併非常簡單,主要難的是,當一棵子樹內每個葉子節點都增加xx時,新的平方和如何計算。
sonison_i表示以點ii爲根的子樹內的葉子節點數,那麼sumisum_i會變成sumi+sonixsum_i+son_i\cdot x,則:
(sumi2)new=(sumi+sonix)2(\sum sum_i^2)_{new}=\sum (sum_i+son_i\cdot x)^2
=sumi2+2xsumisoni+x2soni2=\sum sum_i^2+2x\sum sum_i\cdot son_i+x^2\cdot \sum son_i^2
其中soni2\sum son_i^2怎麼都不會變,一開始處理好就行了,那麼顯然現在要處理sumisoni\sum sum_i\cdot son_i的維護,則:
(sumisoni)new=(sumi+sonix)soni(\sum sum_i\cdot son_i)_{new}=\sum (sum_i+son_i\cdot x)\cdot son_i
=sumisoni+xsoni2=\sum sum_i\cdot son_i+x\sum son_i^2
於是直接線段樹維護即可,時間複雜度爲O(nlogn)O(n\log n)
不過最後一個點比較毒瘤,要使用__int128之類的…然而我並不想影響我代碼的美觀性,所以感受一下感覺就行了…
以下是本人代碼:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
int n,m;
ll a[100010],sum[400010],sumsum[400010];
ll tag[400010]={0},sumson[400010],sonson[400010];

ll power(ll a,ll b)
{
    ll s=1,ss=a;
    while(b)
    {
        if (b&1) s=s*ss%mod;
        ss=ss*ss%mod;
        b>>=1;
    }
    return s;
}

void pushup(int no,int l,int r)
{
    ll son=r-l+1;
    sum[no]=(sum[no<<1]+sum[no<<1|1])%mod;
    sumsum[no]=(sumsum[no<<1]+sumsum[no<<1|1]+sum[no]*sum[no]%mod)%mod;
    sumson[no]=(sumson[no<<1]+sumson[no<<1|1]+sum[no]*son%mod)%mod;
    sonson[no]=(sonson[no<<1]+sonson[no<<1|1]+son*son%mod)%mod;
}

void update(int no,int l,int r,ll x)
{
    ll son=r-l+1;
    tag[no]=(tag[no]+x)%mod;
    sum[no]=(sum[no]+son*x)%mod;
    sumsum[no]=(sumsum[no]+2ll*x*sumson[no]%mod+x*x%mod*sonson[no]%mod)%mod;
    sumson[no]=(sumson[no]+x*sonson[no]%mod)%mod;
}

void pushdown(int no,int l,int r)
{
    int mid=(l+r)>>1;
    if (tag[no])
    {
        update(no<<1,l,mid,tag[no]);
        update(no<<1|1,mid+1,r,tag[no]);
        tag[no]=0;
    }
}

void buildtree(int no,int l,int r)
{
    if (l==r)
    {
        sum[no]=a[l]%mod;
        sumsum[no]=a[l]*a[l]%mod;
        sumson[no]=a[l]%mod;
        sonson[no]=1ll;
        return;
    }
    int mid=(l+r)>>1;
    buildtree(no<<1,l,mid);
    buildtree(no<<1|1,mid+1,r);
    pushup(no,l,r);
}

void modify(int no,int l,int r,int s,int t,ll x)
{
    if (l>=s&&r<=t)
    {
        update(no,l,r,x);
        return;
    }
    int mid=(l+r)>>1;
    pushdown(no,l,r);
    if (s<=mid) modify(no<<1,l,mid,s,t,x);
    if (t>mid) modify(no<<1|1,mid+1,r,s,t,x);
    pushup(no,l,r);
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    
    buildtree(1,1,n);
    for(int i=1;i<=m;i++)
    {
        int op;
        scanf("%d",&op);
        if (op==1)
        {
            int l,r;
            ll x;
            scanf("%d%d%lld",&l,&r,&x);
            modify(1,1,n,l,r,x);
        }
        if (op==2) printf("%lld\n",sumsum[1]*power(sum[1],mod-2)%mod);
    }
    
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章