[BJOI2018] 链上二次求和
一、题目
二、解法
把答案转化成前缀和的形式,设 表示原数组的一阶前缀和,那么答案是:
设 表示原数组的二阶前缀和(也就是对前缀和再做一次前缀和),答案可以写成:
所以我们只需要拿一棵线段树维护原数组的二阶前缀和即可。考虑区间修改 对于二阶前缀和的影响,通过贡献法可以计算出变化为(设 ):
- 如果 , 会增加
- 如果 , 会增加
令 ,在线段树上维护这三项即可,所有修改操作都可以转化成对于这三项的修改。
比如第一种操作,,那么
时间复杂度
#include <cstdio>
#include <iostream>
using namespace std;
const int M = 800005;
#define int long long
const int MOD = 1e9+7;
const int inv2 = (MOD+1)/2;
const int inv6 = 166666668;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,s[M],a[M],b[M],c[M],t[M];
void add(int &x,int y) {x=(x+y)%MOD;}
int s1(int n) {return n*(n+1)/2%MOD;}
int s2(int n) {return n*(n+1)%MOD*(2*n+1)%MOD*inv6%MOD;}
void get(int i,int l,int r,int da,int db,int dc)
{
add(a[i],da);add(b[i],db);add(c[i],dc);
add(t[i],da*(s2(r)-s2(l-1))+db*(s1(r)-s1(l-1))+dc*(r-l+1));
}
void down(int i,int l,int r)
{
if(!a[i] && !b[i] && !c[i]) return ;
int mid=(l+r)>>1;
get(i<<1,l,mid,a[i],b[i],c[i]);
get(i<<1|1,mid+1,r,a[i],b[i],c[i]);
a[i]=b[i]=c[i]=0;
}
void build(int i,int l,int r)
{
if(l==r) {t[i]=s[l];return ;}
int mid=(l+r)>>1;
build(i<<1,l,mid);
build(i<<1|1,mid+1,r);
t[i]=(t[i<<1]+t[i<<1|1])%MOD;
}
void upd(int i,int l,int r,int L,int R,int a,int b,int c)
{
if(L>r || l>R) return ;
if(L<=l && r<=R) {get(i,l,r,a,b,c);return ;}
int mid=(l+r)>>1;down(i,l,r);
upd(i<<1,l,mid,L,R,a,b,c);
upd(i<<1|1,mid+1,r,L,R,a,b,c);
t[i]=(t[i<<1]+t[i<<1|1])%MOD;
}
int ask(int i,int l,int r,int L,int R)
{
if(L>r || l>R) return 0;
if(L<=l && r<=R) return t[i];
int mid=(l+r)>>1;down(i,l,r);
return ask(i<<1,l,mid,L,R)+ask(i<<1|1,mid+1,r,L,R);
}
void Upd(int l,int r,int d)
{
int len=r-l+1;
upd(1,0,n,l,r,d*inv2%MOD,(3-2*l)*d%MOD*inv2%MOD,
(l*l-3*l+2)%MOD*d%MOD*inv2%MOD);
if(r<n) upd(1,0,n,r+1,n,0,len*d%MOD,(s1(len)-len*r)%MOD*d%MOD);
}
int Ask(int l,int r)
{
return ((ask(1,0,n,n,n)%MOD*(r-l+1)-ask(1,0,n,l-1,r-1)
-ask(1,0,n,n-r,n-l))%MOD+MOD)%MOD;
}
signed main()
{
n=read();m=read();
for(int i=1;i<=n;i++)
s[i]=(s[i-1]+read())%MOD;
for(int i=1;i<=n;i++)
s[i]=(s[i-1]+s[i])%MOD;
build(1,0,n);
while(m--)
{
int op=read(),l=read(),r=read();
if(l>r) swap(l,r);
if(op==1) Upd(l,r,read());
else printf("%lld\n",Ask(l,r));
}
}
分类:
数据结构-----线段树
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
2021-07-25 CF1349F1 Slime and Sequences