P2221 [HAOI2012]高速公路 & zloj 练习18 D
written on 2022-05-16
此题关键在于模拟过程,写出每个询问的通式。
亲手模拟后,发现对于给定的询问 \(l,r\) ,我们只需求出 \(\sum_{i=l+1}^{r} a_i*(i-l)*(r-i+1)\) 。
单独一个这个式子肯定不好做,所以化简,找出不变的项、会变的项,然后均用线段树维护。
化简过程与化简结果:如这篇题解(感谢 @Kelin)
PS:建议手推一下公式,自行整理系数,加强理解。
发现我们只需维护三个值: \(a_i\) , \(a_i \times i\) , \(a_i \times i^2\) 。
思考一下区间更新的方式(懒标记下传过程中的更新),这时也建议手推一下加深理解,其实本质很简单,就是乘法分配律的应用。
另外附有平方和公式:\(\sum_{i=1}^{n} i \times i=n \times (n-1) \times (2n-1)\)
不懂的可以看代码。
#include<bits/stdc++.h>
#define N 100005
using namespace std;
typedef long long ll;
int n,m;
ll S1(ll l,ll r){return (l+r)*(r-l+1)/2;}
ll S2(ll l,ll r)
{
++l,++r;
return (r*(r-1)*(2*r-1)-l*(l-1)*(2*l-1))/6;
}
struct Seg
{
ll val[3][N<<2],add[N<<2];
void push_up(int p){for(int i=0;i<3;i++) val[i][p]=val[i][p<<1]+val[i][p<<1|1];}
void spread(int p,int l,int r)
{
if(!add[p]) return ;
int mid=l+r>>1;
add[p<<1]+=add[p],add[p<<1|1]+=add[p];
val[0][p<<1]+=add[p]*(mid-l+1),val[0][p<<1|1]+=add[p]*(r-mid);
val[1][p<<1]+=add[p]*S1(1ll*l,1ll*mid),val[1][p<<1|1]+=add[p]*S1(1ll*(mid+1),1ll*r);
val[2][p<<1]+=add[p]*S2(1ll*(l-1),1ll*mid),val[2][p<<1|1]+=add[p]*S2(1ll*mid,1ll*r);
add[p]=0;
}
void update(int p,int l,int r,int L,int R,ll v)
{
if(L<=l&&R>=r)
{
val[0][p]+=v*(r-l+1);
val[1][p]+=v*S1(1ll*l,1ll*r);
val[2][p]+=v*S2(1ll*(l-1),1ll*r);
add[p]+=v;
return ;
}
spread(p,l,r);
int mid=l+r>>1;
if(L<=mid) update(p<<1,l,mid,L,R,v);
if(R>mid) update(p<<1|1,mid+1,r,L,R,v);
push_up(p);
}
ll ask(int p,int l,int r,int L,int R,int id)
{
if(L<=l&&R>=r) return val[id][p];
spread(p,l,r);
int mid=l+r>>1;
ll res=0;
if(L<=mid) res+=ask(p<<1,l,mid,L,R,id);
if(R>mid) res+=ask(p<<1|1,mid+1,r,L,R,id);
return res;
}
}t1;
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
char op[3];
ll l,r,x;
scanf("%s%lld%lld",op,&l,&r);++l;
if(op[0]=='C')
{
scanf("%lld",&x);
t1.update(1,1,n,l,r,x);
// printf("val=%lld\n",t1.ask(1,1,n,2,2,2));
}
else
{
ll val0=t1.ask(1,1,n,l,r,0),val1=t1.ask(1,1,n,l,r,1),val2=t1.ask(1,1,n,l,r,2);
ll ans=(r-l+1-l*r)*val0-val2+(l+r)*val1,all=1ll*(r-l+2)*(r-l+1)/2;
ll g=gcd(ans,all);
printf("%lld/%lld\n",ans/g,all/g);
}
}
}