luogu P2221[HAOI2012]高速公路

题目大意:有一条$n$个点,$n-1$条边的链。求一个区间$[L,R]$内随机选出两点$a,b$的期望距离。要求支持修改边权。

看到区间修改和查询,想到线段树。

考虑一个区间内总距离和:

因为一条边会被一个在它左侧的点和一个在它右侧的点覆盖,所以总距离和可以表示为:

$\sum_{i=l}^{r} a_i*(r-i+1)*(i-l+1)$

如果将式子展开,得到$ \sum {i=l}{r} a_i*(rl+ri+r+il-i*i+1-l)$

将后面的式子按i的次数分类

$\sum_{i=l}^{r} a[i]*(rl+1+r-l)+a[i]*(r+l)*i+a[i]*i*i$

发现后两项在更新时不好计算,所以再额外记录$sum4$记录$\sum i$,$sum5$记录$\sum i*i$,这样在更新的时候可以方便的用这两个常量计算答案。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define mid (l+r)/2
#define N 100005
#define log 40
#define lowbit(x) x&-x
#define mod 998244353
#define int long long
using namespace std;
int sum1[400005],sum2[400005],sum3[400005],sum4[400005],sum5[400005],lazy[400005];
int a[400005];
int ans1,ans2,ans3;
int n,m;
int ls(int x){return x<<1;}
int rs(int x){return x<<1|1;}
void calc1(int now)
{
	sum4[now]=sum4[ls(now)]+sum4[rs(now)];
	sum5[now]=sum5[ls(now)]+sum5[rs(now)];
}
void calc2(int now)
{
	sum1[now]=sum1[ls(now)]+sum1[rs(now)];
	sum2[now]=sum2[ls(now)]+sum2[rs(now)];
	sum3[now]=sum3[ls(now)]+sum3[rs(now)];
}
void build(int now,int l,int r)
{
	if(l==r)
	{
		sum4[now]=l;
		sum5[now]=l*l;
		return;
	}
	build(ls(now),l,mid);
	build(rs(now),mid+1,r);
	calc1(now);
}
void work(int now,int l,int r,int k)
{
	sum1[now]+=k*(r-l+1);
	sum2[now]+=k*sum4[now];
	sum3[now]+=k*sum5[now];
	lazy[now]+=k;
}
void update(int now,int l,int r,int ll,int rr,int k)
{
	if(ll<=l&&r<=rr)
	{
		work(now,l,r,k);
		return;
	}
	if(lazy[now])
	{
		work(ls(now),l,mid,lazy[now]);
		work(rs(now),mid+1,r,lazy[now]);
		lazy[now]=0;
	}
	if(mid>=ll)
	    update(ls(now),l,mid,ll,rr,k);
	if(mid<rr)
	    update(rs(now),mid+1,r,ll,rr,k);
	calc2(now);
}
void query(int now,int l,int r,int ll,int rr)
{
	if(ll<=l&&r<=rr)
	{
		ans1+=sum1[now];
		ans2+=sum2[now];
	    ans3+=sum3[now];
	    return;
	}
	if(lazy[now])
	{
		work(ls(now),l,mid,lazy[now]);
		work(rs(now),mid+1,r,lazy[now]);
		lazy[now]=0;
	}
	if(mid>=ll)
		query(ls(now),l,mid,ll,rr);
	if(mid<rr)
	    query(rs(now),mid+1,r,ll,rr);
}
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int gcd(int a,int b){return(!b)?a:gcd(b,a%b);}
signed main()
{
	scanf("%lld%lld",&n,&m);
	build(1,1,n);
	for(int i=1;i<=m;i++)
	{
		char ch;
		cin>>ch;
		if(ch=='C')
		{
			int l,r,v;
			scanf("%lld%lld%lld",&l,&r,&v);
			r--;
			update(1,1,n,l,r,v);
		}
		else
		{
		    int l,r;
		    scanf("%lld%lld",&l,&r);
		    r--;
		    ans1=ans2=ans3=0;
		    query(1,1,n,l,r);
		    int ans=(r-l+1-r*l)*ans1+(r+l)*ans2-ans3;
		    int q=gcd(ans,(r-l+2)*(r-l+1)/2);
		    printf("%lld/%lld\n",ans/q,(r-l+2)*(r-l+1)/2/q);
		}
	}
	return 0;
}

  

猜你喜欢

转载自www.cnblogs.com/handsome-wjc/p/11264509.html