[HAOI2012] 高速公路
首先,根据题目的数据,我们将边权下放到点权,之后用线段树维护。
对于一个带查询区间 [l,r] ,我们可以依次考虑每个点对答案的贡献。
对于点 i ,经过它的起点有 ( i-l+1 ) 种选择,终点有 ( r-i+1 ) 种选择,根据乘法原理,共有 ( i-l+1 ) * ( r-i+1 ) 条路径经过它。
注意这里,因为我们将边权下放到点权,所以单点依然代表一条路径,不要漏掉。
那么这个区间的所有点的总贡献为 tot = Σ w[i] * ( i-l+1) * ( r-i+1 ),同时易求得共有 ( 1+r-l+1 ) * ( r-l+1 ) / 2 种可能路径,两者相除即为期望值。
现在我们考虑如何求 tot。
按照常规思路,我们提常数项。
tot = Σ w[i] * ( i-l+1) * ( r-i+1 )
= Σ w[i] * ( i*r - i*i + i - l*r + l*i - l + r - i + 1 )
= Σ w[i] * ( ( r-l*r-l+1 )+ ( r+l ) * i - i*i )
= ( r-l*r-l+1) * Σ w[i] + ( r+l ) * Σ w[i] * i - Σ w[i] * i*i
这样一来,我们可以考虑维护 Σ w[i],Σ w[i] * i,Σ w[i] * i*i 这三项来得到答案。
但是这三项好像无法直接维护。
我们再看题目,题目的操作是每次给区间一个增值 x,也就是说对于给定区间,所有 w[i] = x,这样一来我们只需知道 Σ1,Σi,Σi*i,便可以成功维护区间信息,得到解了。
而对于 Σi*i 如何求,数学大佬们都是上公式,像我这种数学渣渣,只会前缀和预处理。
不过前缀和可比公式快多咯。
所以我要再次提醒自己注意中间变量爆精度......!
// q.c
// 中间变量爆精度这种事真的会死人的...
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long LL;
const int M=100000+10;
int n,m; LL s[M][2];
struct Node {
int l,r; LL s1,s2,s3,lazy;
Node():l(0),r(0),s1(0),s2(0),s3(0),lazy(0) {}
};
struct SegmentTree {
int root; Node nd[M<<2];
SegmentTree():root(0) {}
void update(int o) {
Node &p=nd[o],lc=nd[o<<1],rc=nd[o<<1^1];
p.s1=lc.s1+rc.s1;
p.s2=lc.s2+rc.s2;
p.s3=lc.s3+rc.s3;
}
void pushdown(int o) {
if(nd[o].lazy) {
Node &p=nd[o],&lc=nd[o<<1],&rc=nd[o<<1^1];
lc.s1+=(lc.r-lc.l+1)*p.lazy;
lc.s2+=(s[lc.r][0]-s[lc.l-1][0])*p.lazy;
lc.s3+=(s[lc.r][1]-s[lc.l-1][1])*p.lazy;
rc.s1+=(rc.r-rc.l+1)*p.lazy;
rc.s2+=(s[rc.r][0]-s[rc.l-1][0])*p.lazy;
rc.s3+=(s[rc.r][1]-s[rc.l-1][1])*p.lazy;
lc.lazy+=p.lazy;
rc.lazy+=p.lazy;
p.lazy-=p.lazy;
}
}
void build(int o,int l,int r) {
nd[o].l=l,nd[o].r=r;
if(l==r) return ;
int mid=(l+r)>>1;
build(o<<1,l,mid);
build(o<<1^1,mid+1,r);
}
void add(int o,int l,int r,int x) {
Node &p=nd[o];
if(l<=p.l&&p.r<=r) {
p.s1+=(p.r-p.l+1)*(LL)x;
p.s2+=(s[p.r][0]-s[p.l-1][0])*x;
p.s3+=(s[p.r][1]-s[p.l-1][1])*x;
p.lazy+=x;
} else {
pushdown(o);
int mid=(p.l+p.r)>>1;
if(l<=mid) add(o<<1,l,r,x);
if(r>mid) add(o<<1^1,l,r,x);
update(o);
}
}
Node query(int o,int l,int r) {
Node &p=nd[o];
if(l<=p.l&&p.r<=r) return p;
else {
pushdown(o);
int mid=(p.l+p.r)>>1;
Node lc,rc;
if(l<=mid) lc=query(o<<1,l,r);
if(r>mid) rc=query(o<<1^1,l,r);
lc.s1+=rc.s1;
lc.s2+=rc.s2;
lc.s3+=rc.s3;
return lc;
}
}
}t;
LL gcd(LL a,LL b) {
return !b?a:gcd(b,a%b);
}
void solve(int l,int r) {
Node p=t.query(t.root,l,r);
LL x=(r-(LL)l*r-l+1)*p.s1+(r+l)*p.s2-p.s3; // 爆.
LL y=(LL)(r-l+2)*(r-l+1)/2; // 爆.
LL d=gcd(x,y); x/=d,y/=d;
printf("%lld/%lld\n",x,y);
}
int main() {
freopen("roadxw.in","r",stdin);
freopen("roadxw.out","w",stdout);
scanf("%d%d",&n,&m); --n;
for(int i=1;i<=n;i++) {
s[i][0]=s[i-1][0]+(LL)i;
s[i][1]=s[i-1][1]+(LL)i*i;
}
t.build(1,1,n);
char opt[3]; int l,r,x;
for(int i=1;i<=m;i++) {
scanf("%s%d%d",opt,&l,&r);
if(opt[0]=='C') scanf("%d",&x),t.add(t.root,l,r-1,x);
else solve(l,r-1);
}
return 0;
}

浙公网安备 33010602011771号