P2221 [HAOI2012]高速公路 & zloj 练习18 D

written on 2022-05-16

此题关键在于模拟过程,写出每个询问的通式。

亲手模拟后,发现对于给定的询问 \(l,r\) ,我们只需求出 \(\sum_{i=l+1}^{r} a_i*(i-l)*(r-i+1)\)

单独一个这个式子肯定不好做,所以化简,找出不变的项、会变的项,然后均用线段树维护。

化简过程与化简结果:如这篇题解(感谢 @Kelin)

PS:建议手推一下公式,自行整理系数,加强理解。

发现我们只需维护三个值: \(a_i\)\(a_i \times i\)\(a_i \times i^2\)

思考一下区间更新的方式(懒标记下传过程中的更新),这时也建议手推一下加深理解,其实本质很简单,就是乘法分配律的应用。

另外附有平方和公式:\(\sum_{i=1}^{n} i \times i=n \times (n-1) \times (2n-1)\)

不懂的可以看代码。

#include<bits/stdc++.h>
#define N 100005
using namespace std;
typedef long long ll;
int n,m;
ll S1(ll l,ll r){return (l+r)*(r-l+1)/2;}
ll S2(ll l,ll r)
{
	++l,++r;
	return (r*(r-1)*(2*r-1)-l*(l-1)*(2*l-1))/6;
}
struct Seg
{
	ll val[3][N<<2],add[N<<2];
	void push_up(int p){for(int i=0;i<3;i++) val[i][p]=val[i][p<<1]+val[i][p<<1|1];}
	void spread(int p,int l,int r)
	{
		if(!add[p]) return ;
		int mid=l+r>>1;
		add[p<<1]+=add[p],add[p<<1|1]+=add[p];
		val[0][p<<1]+=add[p]*(mid-l+1),val[0][p<<1|1]+=add[p]*(r-mid);
		val[1][p<<1]+=add[p]*S1(1ll*l,1ll*mid),val[1][p<<1|1]+=add[p]*S1(1ll*(mid+1),1ll*r);
		val[2][p<<1]+=add[p]*S2(1ll*(l-1),1ll*mid),val[2][p<<1|1]+=add[p]*S2(1ll*mid,1ll*r);
		add[p]=0;
	}
	void update(int p,int l,int r,int L,int R,ll v)
	{
		if(L<=l&&R>=r)
		{
			val[0][p]+=v*(r-l+1);
			val[1][p]+=v*S1(1ll*l,1ll*r);
			val[2][p]+=v*S2(1ll*(l-1),1ll*r);
			add[p]+=v;
			return ;
		}
		spread(p,l,r);
		int mid=l+r>>1;
		if(L<=mid) update(p<<1,l,mid,L,R,v);
		if(R>mid) update(p<<1|1,mid+1,r,L,R,v);
		push_up(p);
	}
	ll ask(int p,int l,int r,int L,int R,int id)
	{
		if(L<=l&&R>=r) return val[id][p];
		spread(p,l,r);
		int mid=l+r>>1;
		ll res=0;
		if(L<=mid) res+=ask(p<<1,l,mid,L,R,id);
		if(R>mid) res+=ask(p<<1|1,mid+1,r,L,R,id);
		return res;
	}
}t1;
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		char op[3];
		ll l,r,x;
		scanf("%s%lld%lld",op,&l,&r);++l;
		if(op[0]=='C')
		{
			scanf("%lld",&x);
			t1.update(1,1,n,l,r,x);
//			printf("val=%lld\n",t1.ask(1,1,n,2,2,2));
		}
		else
		{
			ll val0=t1.ask(1,1,n,l,r,0),val1=t1.ask(1,1,n,l,r,1),val2=t1.ask(1,1,n,l,r,2);
			ll ans=(r-l+1-l*r)*val0-val2+(l+r)*val1,all=1ll*(r-l+2)*(r-l+1)/2;
			ll g=gcd(ans,all);
			printf("%lld/%lld\n",ans/g,all/g);
		}
	}
}
posted @ 2022-07-31 18:25  Freshair_qprt  阅读(9)  评论(0编辑  收藏  举报