您的位置:首页 > 其它

HDU 5381 The sum of gcd

2016-10-06 21:26 323 查看
题意: 有一列数,Q个询问区间,求所有子区间的GCD值之和。

思路:线段树先处理区间左右端点都在同一线段树结点内的情况,再加上各在左右子树的情况,即得到当前结点的所有子区间GCD之和。

            需要维护[L,R]区间的所有[L,X]区间和[Y,R]的GCD值,可以发现这些不同的GCD不超过log(ai)个,并且具有递减性质。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
int t,n,q,ql,qr;
int st[35],g[35],val[10010],top;
int gcd(int x,int y){
return y==0 ? x:gcd(y,x%y);
}

void insert(int cnt,int gg){
int d=gcd(g[top],gg);
if (d==g[top]) st[top]+=cnt; //区间GCD具有递减性,g是降序数组,st记录对应个数
else top++,g[top]=d,st[top]=cnt;
}
struct node {
int lst[35],lg[35],rst[35],rg[35],ltop,rtop;
long long sum;
void update(node a){
int i,j;
sum+=a.sum; //l,r都在左区间+都在有区间
for (i=1;i<=rtop;i++){
for (j=1;j<=a.ltop;j++){
sum+=(long long)gcd(rg[i],a.lg[j])*rst[i]*a.lst[j]; //l.r分别落在两个子区间的情况
}
}

for (i=1;i<=ltop;i++) st[i]=lst[i],g[i]=lg[i]; //更新L为区间左端点的GCD情况
top=ltop;
for (j=1;j<=a.ltop;j++) insert(a.lst[j],a.lg[j]);
for (i=1;i<=top;i++) lst[i]=st[i],lg[i]=g[i];
ltop=top;

for (i=1;i<=a.rtop;i++) st[i]=a.rst[i],g[i]=a.rg[i]; //更新R为区间右端点的GCD情况
top=a.rtop;
for (j=1;j<=rtop;j++) insert(rst[j],rg[j]);
for (i=1;i<=top;i++) rst[i]=st[i],rg[i]=g[i];
rtop=top;

}
} tree[10010*4],ans;

void build(int o,int l,int r){
if (l==r) {
tree[o].ltop=tree[o].rtop=1;
tree[o].lst[1]=tree[o].rst[1]=1;
tree[o].lg[1]=tree[o].rg[1]=val[l];
tree[o].sum=val[l];
} else {
int mid=(l+r)/2;
build(o*2,l,mid);
build(o*2+1,mid+1,r);
tree[o]=tree[o*2];
tree[o].update(tree[2*o+1]);
}
}

void query(int o,int l,int r){
if (ql<=l && r<=qr){
if (l==ql) ans=tree[o];
else ans.update(tree[o]);
} else {
int mid=(l+r)/2;
if (ql<=mid) query(o*2,l,mid);
if (qr>mid) query(o*2+1,mid+1,r);
}
}
int main()
{
scanf("%d",&t);
int i;
while (t--){
scanf("%d",&n);
for (i=1;i<=n;i++) scanf("%d",&val[i]);
build(1,1,n);
scanf("%d",&q);
while (q--){
scanf("%d %d",&ql,&qr);
ans.sum=0;
query(1,1,n);
printf("%I64d\n",ans.sum);
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: