bzoj2752 高速公路(road) 线段树

       PS(请无视):天哪第一次评测窝调试中间输出没删你告诉我RE!!。。还好我无意瞟到了不然还有完没完啊

       我们发现第N个收费站是没有用的。。然后在操作时将r--简化操作。首先那个期望是很坑爹的。实际上,期望可以用总和/总状态数解决,我们不妨设a<b,这样期望不变(因为a≠b),从而总状态数即(r-l+2)*(r-l+1)/2。然后考虑总和,我们对每一个收费站单独分析。对于收费站i,它对总和的贡献为v[i]*(i-l+1)*(r-i+1),可以发现(i-l+1)*(r-i+1)即经过i的路径的条数。因此查询l,r的总和即Σ(i=l,r) v[i]*(i-l+1)*(r-i+1)。

       然后就变成了区间修改区间查询的经典线段树题目了(不过好像并没有什么转化)

       但是直接维护答案(或者用几个辅助数组)是会写疯掉的。。(←亲身体验)。我们把查询的那个式子展开,然后以i为主元降幂排列把那个式子化成:

       Σ(i=l,r) -i^2 *a[i]+i*a[i]*(l+r)-(r+1)*(l-1)*a[i]=-Σ(i=l,r)-i^2 *a[i]+(l+r)Σ(i=1,r)i*a[i]-(r+1)*(l-1)Σ(i=l,r)a[i]

       然后我们就只需要维护i*i*a[i],i*a[i],a[i]的区间和了。线段树轻松胜任。最后输出的时候再转化一下就好了。

AC代码如下(附一个暴力):

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define N 600005
using namespace std;

int n,m,c[N][2]; ll gas[N],sz[N],add[N];
struct node{ ll t1,t2,t3; }val[N];
int read(){
	int x=0,fu=1; char ch=getchar();
	while (ch<'0' || ch>'9'){ if (ch=='-') fu=-1; ch=getchar(); }
	while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
	return x*fu;
}
void build(int k,int l,int r){
	c[k][0]=l; c[k][1]=r; int mid=(l+r)>>1;
	if (l==r) return; build(k<<1,l,mid); build(k<<1|1,mid+1,r);
}
void maintain(int k){
	int l=k<<1,r=l|1; val[k].t1=val[l].t1+val[r].t1;
	val[k].t2=val[l].t2+val[r].t2; val[k].t3=val[l].t3+val[r].t3;
}
void mdy(int k,ll v){
	int l=c[k][0],r=c[k][1]; add[k]+=v;
	val[k].t2+=v*(gas[r]-gas[l-1]);
	val[k].t1+=v*(sz[r]-sz[l-1]); val[k].t3+=v*(r-l+1);
}
void pushdown(int k){
	if (add[k]){ mdy(k<<1,add[k]); mdy(k<<1|1,add[k]); add[k]=0; }
}
void ins(int k,int x,int y,ll v){
	int l=c[k][0],r=c[k][1],mid=(l+r)>>1;
	if (l==x && r==y){ mdy(k,v); return; }
	pushdown(k);
	if (y<=mid) ins(k<<1,x,y,v); else
	if (x>mid) ins(k<<1|1,x,y,v); else{
		ins(k<<1,x,mid,v); ins(k<<1|1,mid+1,y,v);
	}
	maintain(k);
}	
node qry(int k,int x,int y){
	int l=c[k][0],r=c[k][1],mid=(l+r)>>1;
	if (l==x && r==y) return val[k];
	pushdown(k);
	if (y<=mid) return qry(k<<1,x,y); else
	if (x>mid) return qry(k<<1|1,x,y); else{
		node u=qry(k<<1,x,mid),v=qry(k<<1|1,mid+1,y);
		u.t1+=v.t1; u.t2+=v.t2; u.t3+=v.t3; return u;
	}
}
ll gcd(ll x,ll y){ return (y)?gcd(y,x%y):x; }
int main(){
	n=read(); m=read(); int i;
	build(1,1,n); char ch;
	for (i=1; i<=n; i++){ gas[i]=gas[i-1]+i; sz[i]=sz[i-1]+(ll)i*i; }
	while (m--){
		ch=getchar(); while (ch<'A' || ch>'Z') ch=getchar();
		if (ch=='C'){
			int x=read(),y=read()-1,z=read();
			ins(1,x,y,(ll)z);
		} else{
			int x=read(),y=read()-1; node ans=qry(1,x,y);
			ll fz=ans.t2*(x+y)-ans.t1-ans.t3*(y+1)*(x-1),fm=gas[y-x+1];
			ll tmp=gcd(fz,fm); printf("%lld/%lld\n",fz/tmp,fm/tmp);
		}
	}
	return 0;
}
/*
int main(){
	int n,m,i,a[10]={0},sum; char ch;
	scanf("%d%d",&n,&m);
	while (m--){
		ch=getchar(); while (ch<'A' || ch>'Z') ch=getchar();
		if (ch=='C'){
			int x,y,z; scanf("%d%d%d",&x,&y,&z); y--;
			for (i=x; i<=y; i++) a[i]+=z;
		} else{
			sum=0;int x,y; scanf("%d%d",&x,&y); y--;
			for (i=x; i<=y; i++) sum-=i*i*a[i];
			for (i=x; i<=y; i++) sum+=(x+y)*i*a[i];
			for (i=x; i<=y; i++) sum-=(y+1)*(x-1)*a[i];
				printf("%d\n",sum);
		}
	}
}*/

by lych

2016.2.17

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值