链接:https://ac.nowcoder.com/acm/contest/625/H
来源:牛客网
时间限制:C/C++ 3秒,其他语言6秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld
题目描述
众所周知,在算法竞赛中,出题人对他出的题的难度往往存在错误的估计。比如出题人本想出个中等题,没想到却出成了简单题;本想出个自闭题,结果数据太水变成了签到题。因此,为了让一场比赛能有良好的体验,有一个靠谱的验题人是非常重要的。
CC出好题目后,便拿给小马哥看。不出所料,这些题目小马哥全都是看一眼就会做了。而且,小马哥觉得这些题对于参赛选手来说也太水了(5个签到题哦~)。为了避免发生全场十几个队AK这种紧急事态,小马哥决定把一道签到题改成简单题,于是便有了现在这个题目。
小马哥非常喜欢数论,尤其钟爱“最大公约数”(Greatest Common Divisor,简称GCD)这一概念,因此他打算出一道和GCD有关的题目。小马哥首先给出了含n个正整数的序列a1,a2,⋯,ana1,a2,⋯,an,然后让你考虑该序列的所有子区间的数对应的GCD值,也就是说考虑所有gcd(al,⋯,ar)gcd(al,⋯,ar)的值。显然,这样的值一共有n(n+1)2n(n+1)2个。一个中二出题人可能会让你求这n(n+1)2n(n+1)2数中第k大的数,但幸运的是,小马哥是个正经出题人,因此它只让你求这n(n+1)2n(n+1)2个数之和模109+7109+7的结果。
也就是要求下面这个式子:
ans=(∑nl=1∑nr=lgcd(al,⋯,ar))mod(109+7)ans=(∑l=1n∑r=lngcd(al,⋯,ar))mod(109+7)
小马哥在此预祝大家AK~
输入描述:
第一行是一个整数n (1≤n≤5×105)n (1≤n≤5×105),表示数的个数。
接下来一行给出n个整数,其中第i个整数aiai满足1≤ai≤1091≤ai≤109。
输出描述:
输出一行一个整数,表示ans的值。
示例1
输入
5
16 4 7 21 3
输出
72
题意:中问题意略
思路:第一反应是RMQ+二分。。。结果多了一个log然后就T了。。。
不难发现,当从a[r]开始,到a[r-1],a[r-2],...a[l]的gcd不变时(设为v[r]),我们就能一次统计答案了。由于gcd每一次减少至少/2,所以我们从每个r往前算的时候,最多有log(a[i])个不同的gcd。但是不能每次都一个一个往前找,我们用l[r]表示v[r]向前所能到达的最小下标,后面跳的时候l[i]就可以直接跳到l[l[i]-1]了。
代码:
#include<cstdio>
const int N=500010,P=1000000007;
int n,i,j,ans,a[N],l[N],v[N];
int gcd(int a,int b){return b?gcd(b,a%b):a;}
int main(){
scanf("%d",&n);
for(i=1;i<=n;i++)scanf("%d",&a[i]);
for(i=1;i<=n;i++)for(v[i]=a[i],j=l[i]=i;j;j=l[j]-1){
v[j]=gcd(v[j],a[i]);
while(l[j]>1&&gcd(a[i],v[l[j]-1])==gcd(a[i],v[j]))l[j]=l[l[j]-1];
ans=(1LL*v[j]*(j-l[j]+1)+ans)%P;
}
printf("%d",ans);
}
/*
5
16 4 7 21 3
72
*/
我在比赛的时候蠢蠢的用了HDU 5381 The sum of gcd的莫队做法(m=1,即只有一个1~n的查询),也能卡过。。。不过代码量和上面的解法差别可不是一点了。。。
代码:
#include<stdio.h>
#include<iostream>
#include<vector>
#include<algorithm>
#define ll long long
using namespace std;
#define N 500002
const ll mo=1e9+7;
struct node{
int l;
int r;
int id;
};
struct sign{
int g;
int num;
sign(int tempg=0,int tempnum=0){
g=tempg;
num=tempnum;
}
};
int numa[N];
long long int ansa[N];
struct node querya[N];
int length;
vector<sign> rrgcd,rlgcd,llgcd,lrgcd;
bool cmp(struct node a,struct node b){
if(a.l/length==b.l/length){
return a.r<b.r;
}
else{
return a.l/length<b.l/length;
}
}
int gcd(int a,int b){
int c;
while(b){
c=a%b;
a=b;
b=c;
}
return a;
}
int main(){
int t;
int n;
int q;
t=1;
while(t--){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&numa[i]);
}
q=1;querya[1].l=1;querya[1].r=n;
querya[1].id=1;
for(length=0;length*length<=n;length++);
sort(querya+1,querya+1+q,cmp);
int kuainum=-1,RR=0;
long long ansrr=0;
for(int i=1;i<=q;i++){
//printf("%d %d %d\n",i,querya[i].l,querya[i].r);
if(querya[i].l/length!=kuainum){
kuainum=querya[i].l/length;
RR=(kuainum+1)*length-1;
rrgcd.clear();
rlgcd.clear();
ansrr=0;
}
while(RR<querya[i].r){
RR++;
for(int j=0;j<rrgcd.size();j++){
rrgcd[j].g=gcd(rrgcd[j].g,numa[RR]);
ansrr+=(long long int)rrgcd[j].g*rrgcd[j].num%mo;
ansrr%=mo;
}
rrgcd.push_back(sign(numa[RR],1));
ansrr+=(long long int)numa[RR];
ansrr%=mo;
int nowindex=0;
for(int k=1;k<rrgcd.size();k++){
if(rrgcd[k].g==rrgcd[nowindex].g){
rrgcd[nowindex].num+=rrgcd[k].num;
}
else{
nowindex++;
rrgcd[nowindex]=rrgcd[k];
}
}
while(nowindex+1<rrgcd.size()){
rrgcd.pop_back();//因为gcd是单调减的,所以即使是按顺序加入,未排序,也是按照从大到小的顺序排的
}
if(rlgcd.size()==0){
//printf("%d %d\n",RR,numa[RR]);
rlgcd.push_back(sign(numa[RR],1));
}
else{
int tempid=rlgcd.size();
//printf("%d %d\n",tempid-1,rlgcd[tempid-1].g);
if(!(numa[RR]%rlgcd[tempid-1].g)){
rlgcd[tempid-1].num++;
}
else{//printf("wi shi da hao ren\n");
rlgcd.push_back(sign(gcd(rlgcd[tempid-1].g,numa[RR]),1));
}
}
}
int LR=(kuainum+1)*length-1;
LR=min(LR,querya[i].r);
llgcd.clear();
lrgcd.clear();
long long int ansll=0;
for(;LR>=querya[i].l;LR--){
for(int j=0;j<llgcd.size();j++){
llgcd[j].g=gcd(llgcd[j].g,numa[LR]);
ansll+=(long long int)llgcd[j].g*llgcd[j].num%mo;
ansll%=mo;
}
llgcd.push_back(sign(numa[LR],1));
ansll+=(long long int)numa[LR];
ansll%=mo;
int nowindex=0;
for(int k=1;k<llgcd.size();k++){
if(llgcd[nowindex].g==llgcd[k].g){
llgcd[nowindex].num+=llgcd[k].num;
}
else{
nowindex++;
llgcd[nowindex]=llgcd[k];
}
}
while(nowindex+1<llgcd.size()){
llgcd.pop_back();
}
int tempid=lrgcd.size();
if(tempid==0){
lrgcd.push_back(sign(numa[LR],1));
}
else{
int tempg=gcd(numa[LR],lrgcd[tempid-1].g);
if(tempg==lrgcd[tempid-1].g){
lrgcd[tempid-1].num++;
}
else{
lrgcd.push_back(sign(tempg,1));
}
}
}
int tempid=querya[i].id;
ansa[tempid]=(ansll+ansrr)%mo;
for(int j=0;j<lrgcd.size();j++){
for(int k=0;k<rlgcd.size();k++){
ansa[tempid]+=mo+(long long int)gcd(lrgcd[j].g,rlgcd[k].g)*lrgcd[j].num%mo*rlgcd[k].num%mo;
ansa[tempid]%=mo;
}
}
}
for(int i=1;i<=q;i++){
printf("%lld\n",ansa[i]);
}
}
return 0;
}