8.31做题记录

P5686[CSP-S2019 江西] 和积和

题目背景

JXCSP-S T2

题目描述

给定两个下标从 \(1\)\(n\) 编号的序列 \(a_i,b_i\),定义函数 \(S(l,r)(1\le l\le r\le n)\) 为:

\[\sum_{i=l}^r a_i\times \sum_{i=l}^r b_i \]

请你求出下列式子的值:

\[\sum_{l=1}^n \sum_{r=l}^n S(l,r) \]

由于答案可能很大,你只需要给出答案模 \(10^9+7\) 后的结果。

输入格式

第一行一个正整数 \(n\) 表示序列长度。
第二行 \(n\) 个正整数表示 \(a_i\)
第三行 \(n\) 个正整数表示 \(b_i\)

输出格式

仅一行一个整数表示答案模 \(10^9+7\) 后的结果。

样例 #1

样例输入 #1

3
2 3 4
3 4 5

样例输出 #1

244

样例 #2

样例输入 #2

5
11 22 33 44 55
12 34 56 78 90

样例输出 #2

201542

提示

【数据范围】
对于 \(20\%\) 的数据:\(n\le 10\) , \(a_i,b_i\le 10\)
对于 \(40\%\) 的数据:\(n\le 200\) , \(a_i,b_i\le 100\)
对于 \(70\%\) 的数据:\(n\le 3000\) , \(a_i,b_i\le 10^5\)
对于 \(100\%\) 的数据:\(3\le n\le 5\times 10^5\) , \(1\le a_i,b_i\le 10^9\)

\(description\)

求:

\[\sum_{l=1}^n \sum_{r=l}^n \sum_{i=l}^r a_i\times \sum_{i=l}^r b_i \]

\(solution\)

很明显, \(\sum_{i=l}^r a_i\)\(\sum_{i=l}^r b_i\) 都是可以前缀和预处理到 \(O(1)\) 的。

\(suma\) 数组和 \(sumb\) 数组分别记录前缀和,那我们的式子就转化为:

\[\sum_{l=1}^n \sum_{r=l}^n (suma[r]-suma[l-1]) \times (sumb[r]-sumb[l-1]) \]

时间复杂度 \(O(n^2)\),可以拿到 \(70pts\) 的好成绩。

#include<iostream>
#define int long long
using namespace std;
const int N=5e5+5;
const int M=3005;
const int mod=1e9+7;
int a[N],b[N],suma[N],sumb[N];
int n,ans;
signed main()
{
	scanf("%lld",&n);
	for(int i=1;i<=n;++i)
		scanf("%lld",&a[i]),suma[i]=suma[i-1]+a[i];
	for(int i=1;i<=n;++i)
		scanf("%lld",&b[i]),sumb[i]=sumb[i-1]+b[i];
	for(int i=1;i<=n;++i)
		for(int j=i;j<=n;++j)
			(ans+=(suma[j]-suma[i-1])*(sumb[j]-sumb[i-1]))%=mod;
	printf("%lld",ans);
	return 0;
}

70不少了,跑路吧

我们考虑继续优化。

我们对于式子继续进行变形:

\[\sum_{l=1}^n \sum_{r=l}^n (suma[r]-suma[l-1]) \times (sumb[r]-sumb[l-1]) \]

\[=\sum_{l=1}^n \sum_{r=l}^n suma[r]\times sumb[r]+suma[l-1] \times sumb[l-1]-suma[r] \times sumb[l-1] - suma[l-1] \times sumb[r] \]

\[=\sum_{l=1}^n (\sum_{r=l}^n suma[r] \times sumb[r])+(\sum_{r=l}^n suma[l-1] \times sumb[l-1])-(\sum_{r=l}^n suma[r] \times sumb[l-1])-(\sum_{r=l}^n sumb[r] \times suma[l-1]) \]

\[=\sum_{l=1}^n (\sum_{r=l}^n suma[r] \times sumb[r])+(\sum_{r=l}^n suma[l-1] \times sumb[l-1])-(sumb[l-1] \times \sum_{r=l}^n suma[r])-(suma[l-1] \times \sum_{r=l}^n sumb[r] ) \]

然后我们对于每个部分分别进行预处理:

我们用 \(c\) 数组记录 \(suma[i] \times sumb[i]\),用 \(sumc\) 数组记录 \(c\) 数组的前缀和,那么 \(\sum_{r=l}^n suma[r] \times sumb[r]=sumc[r]-sumc[l-1]\)

\(\sum_{r=l}^n suma[l-1] \times sumb[l-1]\)很明显就是 \(n-i+1\)\(c[l-1]\)

至于 \(sumb[l-1] \times \sum_{r=l}^n suma[r]\),我们可以定义一个 \(A\) 数组记录 \(suma\) 数组的前缀和,那么 \(sumb[l-1] \times \sum_{r=l}^n suma[r]=sumb[l-1] \times (A[r]-A[l-1])\)

同理,我们定义一个 \(B\) 数组记录 \(sumb\)的前缀和,那么 \(suma[l-1] \times \sum_{r=l}^n sumb[r]=suma[l-1] \times (B[r]-B[l-1])\)

所以我们的式子最终被化简为:

\[\sum_{l=1}^n sumc[n]-sumc[l-1]+(n-i+1) \times c[l-1] - sumb[l-1] \times (A[n]-A[l-1]) -suma[l-1] \times (B[n]-B[l-1]) \]

时间复杂度 \(O(n)\)

\(code\)

#include<iostream>
#define int long long
using namespace std;
const int N=5e5+5;
const int mod=1e9+7;
int a[N],b[N],c[N],suma[N],sumb[N],sumc[N],A[N],B[N];
int n,ans;
signed main()
{
	scanf("%lld",&n);
	for(int i=1;i<=n;++i)
		scanf("%lld",&a[i]),suma[i]=(suma[i-1]+a[i])%mod;
	for(int i=1;i<=n;++i)
		scanf("%lld",&b[i]),sumb[i]=(sumb[i-1]+b[i])%mod;
	for(int i=1;i<=n;++i)
		c[i]=(suma[i]*sumb[i])%mod,sumc[i]=(sumc[i-1]+c[i])%mod;
	for(int i=1;i<=n;++i)
		A[i]=(A[i-1]+suma[i])%mod,B[i]=(B[i-1]+sumb[i])%mod;
	for(int i=1;i<=n;++i)
		ans=((ans+sumc[n]-sumc[i-1]+c[i-1]*(n-i+1)%mod-sumb[i-1]*(A[n]-A[i-1])%mod-suma[i-1]*(B[n]-B[i-1])%mod+mod)%mod+mod)%mod;
	printf("%lld",ans);
	return 0;
}

T211027 Prob

题目背景

本题读入量较大,建议使用输入优化

我有个疑惑,希望您能帮我解答。

本题由于特殊原因,不提供样例解释。

题目描述

我给出一个整数 \(x\),ta 希望您能够告知,这个整数是否能表示为两个质数 \(p_1, p_2\) 的和,如果有的话有多少种方式。

小 A 觉得这样太简单了,为了让题目复杂化,ta 定义一种满足 \(p_1 + p_2 = x\) 的表示方式的价值为 \(\operatorname{lcm}(p_1, p_2)\),对于整数 \(a\),它的价值为所有的划分方式的价值和。

形象的说,定义函数 \(g(x)\) 表示整数 \(x\) 的价值,那么

\[g(x)= \sum_{p_1 + p_2 = x,p_1,p_2\in \mathbf P}\operatorname{lcm}(p_1, p_2) \]

(其中 \(\mathbf P\) 为质数集)

小 B 觉得这样还是不够有意思,于是 ta 给了您 \(n\) 个询问,每个询问给您两个整数 \(l, r\),请您给出

\[\sum_{a = l}^r g(a^2 - a + 1) \]

的结果,答案对 \(10^9 + 7\) 取模。

输入格式

第一行一个整数 \(n\),表示询问个数。

接下来 \(n\) 行,每行两个整数 \(l, r\),表示一个询问。

输出格式

对于每个询问,输出一行一个整数,表示对应的答案。

样例 #1

样例输入 #1

2
4 4
7 9

样例输出 #1

22
224

样例 #2

样例输入 #2

1
3 100000

样例输出 #2

359405584

提示

对于 \(50 \%\) 的数据,\(n \le 10^3, l, r \le 100\)

对于所有数据, \(3 \le l, r \le 10^5,n \le 2 \times 10^ 6\)

\(solution\)

一眼结论,两个质数一定没有公因子,所以两个质数的 \(lcm\) 一定是两个质数的乘积。

也就是说,\(g(x)= \sum_{p_1 + p_2 = x,p_1,p_2\in \mathbf P}\operatorname{lcm}(p_1, p_2)\) 等价于 \(g(x)= \sum_{p_1 + p_2 = x,p_1,p_2\in \mathbf P}p1 \times p2\)

但貌似没大有用。

我们来分析一下 \(\sum_{a = l}^r g(a^2 - a + 1)\) ,其中 \(a^2-a+1\) 有什么意义呢???

\(a^2-a+1=a \times (a-1)+1\),从这玩意能得出啥???

好像比较明显的就是奇偶性,因为 \(a\)\(a-1\) 奇偶性一定不同,所以 \(a \times (a-1)\) 一定为偶数(小学数学应该都能理解),所以 \(a \times (a-1)+1\) 一定是一个奇数。

有啥用???

我们都知道唯一一个偶质数是 \(2\)

因为我们 \(g(x)\) 中的 \(x\) 一定为奇数,所以 \(p1,p2\) 当中一定有一个是 \(2\) !!!

因为奇数加奇数一定为偶数,所以要想加出奇数,只能是偶数+奇数,是质数的偶数只有 \(2\)

那现在问题就简单起来了,很明显,每一个 \(x\) 只有一种分解方式, \(2\)\(x-2\) ,我们只需要判断 \(x-2\)是否为质数就行了。

因为要求和,所以直接在预处理时前缀和优化即可。

\(code\)

#include<iostream>
#define int long long
using namespace std;
const int N=1e5+5;
const int mod=1e9+7;
int prime[N],vis[N],ans[N];
int cnt,T;
void get_prime()
{
	vis[1]=1;
	for(int i=2;i<=N;++i)
	{
		if(!vis[i])  prime[++cnt]=i;
		for(int j=1;j<=cnt&&i*prime[j]<=N;++j)
		{
			vis[prime[j]*i]=1;
			if(i%prime[j]==0)  break;
		}
	}
}
bool check(int x)
{
	for(int i=1;i<=cnt&&prime[i]<=x;i++)  if(x!=prime[i]&&x%prime[i]==0)  return false;
	return true;
}
signed main()
{
	get_prime();
	scanf("%lld",&T);
	for(int i=3;i<=N;++i)
	{
		int other=i*i-i+1-2;
		if(check(other))  ans[i]=ans[i-1]+2*other;
		else ans[i]=ans[i-1];
		if(ans[i]>=mod)  ans[i]%=mod;
	}
	while(T--)
	{
		int l,r;
		scanf("%lld %lld",&l,&r);
		printf("%lld\n",((ans[r]-ans[l-1])%mod+mod)%mod);
	}
	return 0;
}
posted @ 2022-08-31 17:52  respect_lowsmile  阅读(27)  评论(0编辑  收藏  举报