8.31做题记录
P5686[CSP-S2019 江西] 和积和
题目背景
JXCSP-S T2
题目描述
给定两个下标从 \(1\) 到 \(n\) 编号的序列 \(a_i,b_i\),定义函数 \(S(l,r)(1\le l\le r\le n)\) 为:
请你求出下列式子的值:
由于答案可能很大,你只需要给出答案模 \(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\)
求:
\(solution\)
很明显, \(\sum_{i=l}^r a_i\) 和 \(\sum_{i=l}^r b_i\) 都是可以前缀和预处理到 \(O(1)\) 的。
用 \(suma\) 数组和 \(sumb\) 数组分别记录前缀和,那我们的式子就转化为:
时间复杂度 \(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不少了,跑路吧
我们考虑继续优化。
我们对于式子继续进行变形:
然后我们对于每个部分分别进行预处理:
我们用 \(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])\)。
所以我们的式子最终被化简为:
时间复杂度 \(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\) 的价值,那么
(其中 \(\mathbf P\) 为质数集)
小 B 觉得这样还是不够有意思,于是 ta 给了您 \(n\) 个询问,每个询问给您两个整数 \(l, r\),请您给出
的结果,答案对 \(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;
}