http://acm.hdu.edu.cn/showproblem.php?pid=5381
一条莫队算法的题。本来也有过这样的考虑,然而因为并不能O(1)得到区间转移的值,所以一开始放弃了。想不到题目数据比较小,卡过去了。。。。
莫队算法本质上就是把原来的查询顺序改变。
先把整个区间分块,每块长度为sqrt(n);
对于左端点在同一区间的询问,我们对他们的右端点排序;
为了叙述方便,我们记一个查询左边界为l,右边界为r;其左端点在第k块,每块长度为len;
查询的时候,分为四部分记录:左端点这一块的gcd和,i到(k+1)*len-1的gcd值(i属于[l,(k+1)*len-1]);
(k+1)*len 到r部分的所有gcd和,(k+1)*len到j的所有gcd值 (j属于[(k+1)*len,r]);(当r<(k+1)*len时特判)
注意到后两部分的求解可以在前一次的计算上叠加,这就是节省时间的来源了
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define maxn 10005
#define ll unsigned long long
ll gcd (ll a,ll b) {
if(b>a) return gcd(b,a);
return b==0?a:gcd(b,a%b);
}
int block;
struct node {
int l,r,id;
bool operator < (const node& an) const {
if(l/block==an.l/block) return r<an.r;
return l<an.l;
}
}query[maxn];
struct hav {
ll g_val;int num;
hav(ll a=0,int b=0):g_val(a),num(b){}
};
vector<hav> lvec,ltrvec,rvec,rtlvec;
ll a[maxn],ans[maxn];
int main ()
{
freopen("aa.txt","r",stdin);
int T,n,q,i,j,k,p,f;
cin>>T;
while(T--) {
cin>>n;block=(int)sqrt((double)n);
for(i=1;i<=n;i++) scanf("%I64u",&a[i]);
cin>>q;
for(i=1;i<=q;i++) {
scanf("%d%d",&query[i].l,&query[i].r);
query[i].id=i;
}
sort(query+1,query+1+q);
int nb=-1,rr;ll res;
for(i=1;i<=q;i++) {
if(query[i].l/block!=nb) {
nb=query[i].l/block;res=0;
rvec.clear();rtlvec.clear();
rr=(nb+1)*block;
}
while(rr<=query[i].r) {
for(j=0;j<rvec.size();j++) {
rvec[j].g_val=gcd(rvec[j].g_val,a[rr]);
// cout<<"j:"<<j<<" val:"<<rvec[j].g_val<<" num:"<<rvec[j].num<<endl;
res+=rvec[j].g_val*rvec[j].num;
}
rvec.push_back(hav(a[rr],1));res+=a[rr];
j=0;k=1;
while(k<rvec.size()) {
if(rvec[j].g_val==rvec[k].g_val) {
rvec[j].num+=rvec[k].num;
}
else {
j++;rvec[j]=rvec[k];
}
k++;
}j++;
while(rvec.size()>j) rvec.pop_back();
if(rtlvec.empty()) {
rtlvec.push_back(hav(a[rr],1));
}
else {
k=rtlvec.size()-1;
ll val=gcd(a[rr],rtlvec[k].g_val);
if(val==rtlvec[k].g_val) rtlvec[k].num++;
else rtlvec.push_back(hav(val,1));
}
rr++;
}
lvec.clear();ltrvec.clear();p=query[i].id;ans[p]=0;
if(query[i].r<(nb+1)*block) {
// cout<<"id:"<<p<<endl;
for(j=query[i].l;j<=query[i].r;j++) {
for(k=0;k<lvec.size();k++) {
lvec[k].g_val=gcd(lvec[k].g_val,a[j]);
ans[p]+=lvec[k].g_val*lvec[k].num;
}
lvec.push_back(hav(a[j],1));ans[p]+=a[j];
k=0;f=1;
while(f<lvec.size()) {
if(lvec[k].g_val==lvec[f].g_val) {
lvec[k].num+=lvec[f].num;
}
else {
k++;lvec[k]=lvec[f];
}
f++;
}k++;
while(lvec.size()>k) lvec.pop_back();
}
}
else {
for(j=(nb+1)*block-1;j>=query[i].l;j--) {
for(k=0;k<lvec.size();k++) {
lvec[k].g_val=gcd(lvec[k].g_val,a[j]);
ans[p]+=lvec[k].g_val*lvec[k].num;
}
lvec.push_back(hav(a[j],1));ans[p]+=a[j];
k=0;f=1;
while(f<lvec.size()) {
if(lvec[k].g_val==lvec[f].g_val) {
lvec[k].num+=lvec[f].num;
}
else {
k++;lvec[k]=lvec[f];
}
f++;
}k++;
while(lvec.size()>k) lvec.pop_back();
if(ltrvec.empty()) ltrvec.push_back(hav(a[j],1));
else {
k=ltrvec.size()-1;
ll val=gcd(a[j],ltrvec[k].g_val);
if(val==ltrvec[k].g_val) ltrvec[k].num++;
else ltrvec.push_back(hav(val,1));
}
}
ans[p]+=res;
for(j=0;j<ltrvec.size();j++) for(k=0;k<rtlvec.size();k++) {
ans[p]+=ltrvec[j].num*gcd(ltrvec[j].g_val,rtlvec[k].g_val)*rtlvec[k].num;
}
}
}
for(i=1;i<=q;i++) printf("%I64u\n",ans[i]);
}
return 0;
}