II.[HAOI2012]高速公路
本题已经在我的任务列表里挂了1年多了
我们将这里的“期望”转成“\(\dfrac{\text{区间中任意两点间距离之和}}{\text{区间中选取两点的方案数}}\)。然后,我们考虑使用线段树维护区间中任意两点间距离之和。
在每个节点上,我们维护如下东西:
\(sum\):区间和
\(len\):区间长度
\(tag\):区间懒标记
\(pre\):区间中每个点到区间中第一个点的距离之和
\(suf\):区间中每个点到区间中最后一个点的距离之和
\(all\):区间中任意两点间距离之和
这个可以通过简单拼凑得到。
时间复杂度\(O(n\log n)\)。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m;
ll gcd(ll x,ll y){return y?gcd(y,x%y):x;}
#define lson x<<1
#define rson x<<1|1
#define mid ((l+r)>>1)
#define C(x) (1ll*((x)+1)*(x)/2)
struct SegTree{
int sum,len,tag;
ll all,pre,suf;
SegTree(){sum=len=tag=all=pre=suf=0;}
friend SegTree operator +(const SegTree &x,const SegTree &y){
SegTree z;
z.len=x.len+y.len;
z.sum=x.sum+y.sum;
z.pre=x.pre+y.pre+1ll*x.sum*y.len;
z.suf=y.suf+x.suf+1ll*y.sum*x.len;
z.all=x.all+y.all+x.suf*y.len+y.pre*x.len;
return z;
}
void ADD(int x){
sum+=x*len;
tag+=x;
pre+=C(len)*x;
suf+=C(len)*x;
all+=1ll*len*(len+1)*(len+2)/6*x;
}
}seg[400100];
#define pushdown(x) seg[lson].ADD(seg[x].tag),seg[rson].ADD(seg[x].tag),seg[x].tag=0
void build(int x,int l,int r){
seg[x].len=r-l;
if(l+1==r)return;
build(lson,l,mid),build(rson,mid,r);
}
void modify(int x,int l,int r,int L,int R,int val){
if(l>=R||r<=L)return;
if(L<=l&&r<=R){seg[x].ADD(val);return;}
pushdown(x),modify(lson,l,mid,L,R,val),modify(rson,mid,r,L,R,val),seg[x]=seg[lson]+seg[rson];
}
SegTree query(int x,int l,int r,int L,int R){
if(l>=R||r<=L)return SegTree();
if(L<=l&&r<=R)return seg[x];
pushdown(x);
return query(lson,l,mid,L,R)+query(rson,mid,r,L,R);
}
void iterate(int x,int l,int r){
printf("[%d,%d):%d,%lld,%lld,%lld\n",l,r,seg[x].sum,seg[x].pre,seg[x].suf,seg[x].all);
if(l+1<r)pushdown(x),iterate(lson,l,mid),iterate(rson,mid,r),seg[x]=seg[lson]+seg[rson];
printf("[%d,%d):%d,%lld,%lld,%lld\n",l,r,seg[x].sum,seg[x].pre,seg[x].suf,seg[x].all);
}
char c[10];
int main(){
scanf("%d%d",&n,&m),build(1,1,n);
for(int i=1,l,r,v;i<=m;i++){
scanf("%s%d%d",c,&l,&r);
if(c[0]=='C')scanf("%d",&v),modify(1,1,n,l,r,v);
else{
ll x=query(1,1,n,l,r).all,y=C(r-l);
ll GCD=gcd(y,x);
printf("%lld/%lld\n",x/GCD,y/GCD);
}
// iterate(1,1,n);
}
return 0;
}