[NOIP2018校模拟赛]T1聚会 party

题目链接:###

聚会

分析:###

设每个点到1号点的距离为dist_{i},每个点的权值为x_{i},目标点到1号点的距离为dist,权值为x,那么对于每一次查询,我们讨论三种情况:

① 目标家庭在区间左边(x<=l)
如图所示

    这种情况下
     ans=sum((dist_{i}-dist)*x_{i])
                         =sum(dist_{i]*x_{i}) - dist*sum(x_{i})

②目标家庭在区间右边(x>=r)

    容易同理得到
    ans= dist*sum(x_{i})-sum(dist_{i]*x_{i}) 

③目标家庭在区间中间(l<x<r)

    将区间从目标家庭处分开,分别求左右子区间的ans1,ans2,过程同①,②

为了降低时间复杂度,每个点到1号点的距离,每个点的权值,以及前两项的乘积都用前缀和来存储,于是我们维护三个前缀和数组——代码中分别是dist,b,p,这样对于每次查询的时间复杂度是O(1)的,总时间复杂度为O(N)。
被坑到的点:
相减的时候可能出现负值,对应的余数也会变成负值,这时候加一个特判

if(ans<0)ans+=mod;

即可。(不加会见祖宗你信吗)

代码如下:

#include<bits/stdc++.h>
#define frog 19260817
using namespace std;
inline long long read(){
	int cnt=0,f=1;char c;
	c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){cnt=cnt*10+c-'0';c=getchar();}
	return cnt*f;
}
long long n,m,dist[200005],x=0,a,l,r;
long long p[200005],b[200005];
long long ans;
int main(){
	n=read();m=read();
	memset(dist,0,sizeof(dist));
	memset(p,0,sizeof(p));
	memset(b,0,sizeof(b));
	for(register int i=2;i<=n;i++){
		x=read();
		dist[i]=(x+dist[i-1])%frog;
	}
	for(register int i=1;i<=n;i++){
		x=read();
		b[i]=(x+b[i-1])%frog;
		p[i]=(p[i-1]+x*dist[i])%frog;
	}
	for(register int i=1;i<=m;i++){
		a=read();l=read();r=read();
		if(a<=l){
			long long t1=(p[r]-p[l-1])%frog;
			long long t2=((b[r]-b[l-1])*dist[a])%frog;
			ans=(t1-t2)%frog;
		}
		if(a>=r){
			long long t1=(p[r]-p[l-1])%frog;
			long long t2=((b[r]-b[l-1])*dist[a])%frog;
			ans=(t2-t1)%frog;
		}
		if(l<a&&a<r){
			long long t1=(p[a]-p[l-1])%frog;
			long long t2=((b[a]-b[l-1])*dist[a])%frog;
			long long t3=(p[r]-p[a])%frog;
			long long t4=((b[r]-b[a])*dist[a])%frog;
			ans=((t2-t1+t3-t4)%frog)%frog;
		}
		if(ans<0)ans+=frog;
		printf("%lld
",ans);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/kma093/p/9737502.html