[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;
}

 

posted @ 2018-04-16 07:31  qjs12  阅读(109)  评论(0编辑  收藏  举报