洛谷2221 [HAOI2012]高速公路(线段树)

传送门

【题目分析】

蒟蒻考完只能刷水题,还被水题翻来覆去的吊打qwq

对于给定区间[l,r],考虑计算它的期望费用,就是 总的费用/总的情况数

总的情况数很好确定,我选择规定可以第一次选的比第二次大,所以计算总贡献后要乘2。

然后考虑计算总的费用。

考虑一个点i,i\in [l,r],那么他能产生的贡献就是包含该点的区间数*该点费用,总的费用公式就是下面这个式子:

\Sigma^{r}_{i=l}v_{i}*(r-i)*(i-l+1),然后把括号打开,得到\Sigma^{r}_{i=l}v_{i}*(r*i-r*l+r-i*i+i*l-i)

整理一下:

\Sigma^{r}_{i=l}v_{i}*r*(1-l)+v_{i}*(r+l-1)*i-v_{i}*i*i

所以很明显了,线段树维护v_{i},v_{i}*i,v_{i}*i^{2}就行了。

菜的忘记区间加要乘上一个区间长度了。。。qwq

【代码~】

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL MAXN=1e5+10;

LL n,q;
LL a[MAXN];
struct Tree{
	LL add;
	LL l,r,pre1,pre2;
	LL sum1,sum2,sum3;
}tr[MAXN<<2];
struct data{
	LL ans1,ans2,ans3;
	data(LL x=0,LL y=0,LL z=0){
		ans1=x,ans2=y,ans3=z;
	}
	friend inline data operator+(const data &a,const data &b){
		return data(a.ans1+b.ans1,a.ans2+b.ans2,a.ans3+b.ans3);
	}
	void init(){
		ans1=ans2=ans3=0;
	}
}ans;

LL Read(){
	LL i=0,f=1;
	char c;
	for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
	if(c=='-')
	  f=-1,c=getchar();
	for(;c>='0'&&c<='9';c=getchar())
	  i=(i<<3)+(i<<1)+c-'0';
	return i*f;
}

void push_up(LL root){
	tr[root].sum1=tr[root<<1].sum1+tr[root<<1|1].sum1;
	tr[root].sum2=tr[root<<1].sum2+tr[root<<1|1].sum2;
	tr[root].sum3=tr[root<<1].sum3+tr[root<<1|1].sum3;
}

void push_now(LL root,LL v){
	tr[root].add+=v;
	tr[root].sum1+=v*(tr[root].r-tr[root].l+1);
	tr[root].sum2+=v*tr[root].pre1;
	tr[root].sum3+=v*tr[root].pre2;
}

void push_down(LL root){
	if(tr[root].add!=0){
		push_now(root<<1,tr[root].add);
		push_now(root<<1|1,tr[root].add);
		tr[root].add=0;
	}
}

void build(LL root,LL l,LL r){
	tr[root].l=l,tr[root].r=r;
	if(l==r){
		tr[root].pre1=l;
		tr[root].pre2=l*l;
		return ;
	}
	LL mid=l+r>>1;
	build(root<<1,l,mid);
	build(root<<1|1,mid+1,r);
	push_up(root);
	tr[root].pre1=tr[root<<1].pre1+tr[root<<1|1].pre1;
	tr[root].pre2=tr[root<<1].pre2+tr[root<<1|1].pre2;
}

void update(LL root,LL l,LL r,LL L,LL R,LL key){
	if(l>R||r<L)
	  return ;
	if(L<=l&&r<=R){
		push_now(root,key);
		return ;
	}
	push_down(root);
	LL mid=l+r>>1;
	if(R<=mid)
	  update(root<<1,l,mid,L,R,key);
	else{
		if(L>mid)
		  update(root<<1|1,mid+1,r,L,R,key);
		else
		  update(root<<1,l,mid,L,mid,key),update(root<<1|1,mid+1,r,mid+1,R,key);
	}
	push_up(root);
}

void query(LL root,LL l,LL r,LL L,LL R){
	if(l>R||r<L)
	  return ;
	if(L<=l&&r<=R){
		ans=ans+data(tr[root].sum1,tr[root].sum2,tr[root].sum3);
		return ;
	}
	push_down(root);
	data a;
	LL mid=l+r>>1;
	if(R<=mid){
		query(root<<1,l,mid,L,R);
		return ;
	}
	else{
		if(L>mid){
		  	query(root<<1|1,mid+1,r,L,R);
		  	return ;
		}
		else{
		    query(root<<1,l,mid,L,mid),query(root<<1|1,mid+1,r,mid+1,R);
		    return ;
		}
	}
}

LL gcd(LL x,LL y){
	return x%y?gcd(y,x%y):y;
}

int main(){
	n=Read(),q=Read();
	build(1,1,n-1);
	while(q--){
		char cz[5];
		LL l,r;
		scanf("%s",cz);
		l=Read(),r=Read();
		if(cz[0]=='C'){
			LL key=Read();
			update(1,1,n-1,l,r-1,key);
		}
		else{
			ans.init();
			query(1,1,n-1,l,r-1);
			LL fz=2*(-r*(l-1)*ans.ans1+(r+l-1)*ans.ans2-ans.ans3);
			LL fm=(r-l+1)*(r-l);
			LL gc=gcd(fz,fm);
			cout<<fz/gc<<'/'<<fm/gc<<'\n';
		}
	}
	return 0;
}

 

posted @ 2018-11-23 21:13  Ishtar~  阅读(119)  评论(0编辑  收藏  举报