测试地址:高速公路
做法:本题需要用到线段树。
归纳一下,本题要维护的信息是:
把提出来,将剩下的式子展开得:
这个式子也就等于:
那么我们只需要维护,和即可。
问题来了,怎么在修改时维护形如的信息?
其实很简单,将展开得,那么我们只需要预处理出的前缀和即可。
那么我们处理的情况,然后就可以维护了。最后要输出一个分数,分子是上面我们化出来的式子,而分母就是我们一开始提出来的那个,约分后输出即可。
以下是本人代码:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
int n,m;
ll sum[400010][3]={0},p[400010]={0},lftsum[100010][3]={0};
ll gcd(ll a,ll b)
{
return (b==0)?a:gcd(b,a%b);
}
void pushdown(int no,int l,int r)
{
if (p[no]!=0)
{
int mid=(l+r)>>1;
for(int i=0;i<3;i++)
{
sum[no<<1][i]+=p[no]*(lftsum[mid][i]-lftsum[l-1][i]);
sum[no<<1|1][i]+=p[no]*(lftsum[r][i]-lftsum[mid][i]);
}
p[no<<1]+=p[no],p[no<<1|1]+=p[no];
p[no]=0;
}
}
void pushup(int no)
{
for(int i=0;i<3;i++)
sum[no][i]=sum[no<<1][i]+sum[no<<1|1][i];
}
void modify(int no,int l,int r,int s,int t,ll c)
{
if (l>=s&&r<=t)
{
for(int i=0;i<3;i++)
sum[no][i]+=c*(lftsum[r][i]-lftsum[l-1][i]);
p[no]+=c;
return;
}
ll mid=(l+r)>>1;
pushdown(no,l,r);
if (s<=mid) modify(no<<1,l,mid,s,t,c);
if (t>mid) modify(no<<1|1,mid+1,r,s,t,c);
pushup(no);
}
void query(int no,int l,int r,int s,int t,ll &sum0,ll &sum1,ll &sum2)
{
if (l>=s&&r<=t)
{
sum0+=sum[no][0];
sum1+=sum[no][1];
sum2+=sum[no][2];
return;
}
int mid=(l+r)>>1;
pushdown(no,l,r);
if (s<=mid) query(no<<1,l,mid,s,t,sum0,sum1,sum2);
if (t>mid) query(no<<1|1,mid+1,r,s,t,sum0,sum1,sum2);
}
int main()
{
scanf("%d%d",&n,&m);
for(ll i=1;i<=n;i++)
{
lftsum[i][0]=lftsum[i-1][0]+1;
lftsum[i][1]=lftsum[i-1][1]+i;
lftsum[i][2]=lftsum[i-1][2]+i*i;
}
for(int i=1;i<=m;i++)
{
char op[3];
ll l,r,v;
scanf("%s",op);
if (op[0]=='C')
{
scanf("%lld%lld%lld",&l,&r,&v);
modify(1,1,n-1,l,r-1,v);
}
if (op[0]=='Q')
{
scanf("%lld%lld",&l,&r);
ll sum0=0,sum1=0,sum2=0,ans1,ans2,g;
query(1,1,n-1,l,r-1,sum0,sum1,sum2);
ans1=-sum2+(l+r-1)*sum1+(-l*r+r)*sum0;
ans2=(r-l+1)*(r-l)/2;
g=gcd(ans1,ans2);
ans1/=g,ans2/=g;
printf("%lld/%lld
",ans1,ans2);
}
}
return 0;
}