測試地址:夢美的線段樹
做法: 本題需要用到線段樹。
經過簡單的計算,題目所求的是:
於是我們現在要求的就是。
向上合併非常簡單,主要難的是,當一棵子樹內每個葉子節點都增加時,新的平方和如何計算。
令表示以點爲根的子樹內的葉子節點數,那麼會變成,則:
其中怎麼都不會變,一開始處理好就行了,那麼顯然現在要處理的維護,則:
於是直接線段樹維護即可,時間複雜度爲。
不過最後一個點比較毒瘤,要使用__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;
}