hdu 5381 The sum of gcd

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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值