HDU5381(莫队算法+ST维护)

解题思路:写了两个小时才写出来,代码能力还有待加强。这道题一开始使用ST表进行维护,用于查询区间范围内的gcd值。但是莫队算法移动一格还有很多区间gcd累加的,要是一个个加必定超时。但是我们可以看出在一定范围内有不少区间的gcd值是一样的,不必一个个数,所以用二分搜索的方式查找,复杂度为O(logn)。我一开始没有事先记录下vl和vr(相同节点),而是要用的时候再查询,这样,复杂度就变成O(log^2n)了,再加上莫队算法的复杂度,就是O(n*sqrt(n)*log^2n),就超时了。但是如果事先用vl,vr记入下来,复杂度就是O(max(n*sqrt(n)*logn , n*log^2(n))),复杂度较之前面小了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<vector>
#define N 10008
using namespace std;
struct node
{
	int l,r,id;
}q[N];
int s[N],be[N];
long long ans,res[N];
vector<pair<int,int> > vl[N],vr[N];
int l1,r1,n;
int p[N][18];
bool cmp(node a,node b)
{
	if(be[a.l]==be[b.l]) return a.r<b.r;
	return a.l<b.l;
}
int gcd(int a,int b)
{
	if(b==0) return a;
	return gcd(b,a%b);
}
void build_st()
{
	for(int i=1;i<=n;i++) p[i][0]=s[i];//必须从1开始,代表第一位 
	for(int j=1;j<=17;j++)
	{
		for(int i=1;i+(1<<j)-1<=n;i++)
		{
			p[i][j]=gcd(p[i][j-1],p[i+(1<<(j-1))][j-1]);
		}
	}
} 
int query(int l,int r)
{
	int k=log2(r-l+1);
	return gcd(p[l][k],p[r-(1<<k)+1][k]);
}
int schl(int l,int r)
{
	int t=query(l1,l),mid;
	int as=l;
	while(r>=l)
	{
		mid=(l+r)/2;
		if(query(l1,mid)!=t)
		{
			r=mid-1;
		}else
		{
			l=mid+1;
			as=max(as,mid);
		}
	}
	return as;
}
int schr(int l,int r)
{
	int t=query(r,r1),mid;
	int as=r;
	while(r>=l)
	{
		mid=(l+r)/2;
		if(query(mid,r1)!=t)
		{
			l=mid+1;
		}else
		{
			r=mid-1;
			as=min(as,mid);
		}
	}
	return as;
}
void addl(int l,int r)
{
	int l1=l;
	for(int i=0;i<vl[l1].size();i++)
	{
		ans+=(long long)(min(r,vl[l1][i].first)-l+1)*vl[l1][i].second;
		l=vl[l1][i].first+1;
		if(vl[l1][i].first>=r) break;
	}
}
void dell(int l,int r)
{
	int l1=l;
	for(int i=0;i<vl[l1].size();i++)
	{
		ans-=(long long)(min(r,vl[l1][i].first)-l+1)*vl[l1][i].second;
		l=vl[l1][i].first+1;
		if(vl[l1][i].first>=r) break;
	}
}
void addr(int l,int r)
{
	int r1=r;
	for(int i=0;i<vr[r1].size();i++)
	{
		ans+=(long long)(r-max(l,vr[r1][i].first)+1)*vr[r1][i].second;
		r=vr[r1][i].first-1;
		if(vr[r1][i].first<=l) break;
	}
}
void delr(int l,int r)
{
	int r1=r;
	for(int i=0;i<vr[r1].size();i++)
	{
		ans-=(long long)(r-max(l,vr[r1][i].first)+1)*vr[r1][i].second;
		r=vr[r1][i].first-1;
		if(vr[r1][i].first<=l) break;
	}
}
int main()
{
	//freopen("t.txt","r",stdin);
	int m,T,bck;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		bck=sqrt(n);
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&s[i]);
			be[i]=i/bck+1;
		}
		scanf("%d",&m);
		for(int i=1;i<=m;i++)
		{
			scanf("%d%d",&q[i].l,&q[i].r);
			q[i].id=i;
		}
		build_st();
		for(int i=n;i>=1;i--)
		{
			vr[i].clear();
			int r=i,l=1;
			r1=i;
			while(r>=l)
			{
				int t=schr(l,r);
				vr[i].push_back(make_pair(t,query(t,i)));
				r=t-1;
			}
		}
		for(int i=1;i<=n;i++)
		{
			vl[i].clear();
			int r=n,l=i;
			l1=i;
			while(r>=l)
			{
				int t=schl(l,r);
				vl[i].push_back(make_pair(t,query(i,t)));
				l=t+1;
			}
		}
		sort(q+1,q+1+m,cmp);
		int l=1,r=0;
		ans=0;
		for(int i=1;i<=m;i++)
		{
			//cout<<q[i].l<<" "<<q[i].r<<"*"<<endl;
			while(l>q[i].l) addl(l-1,r),l--;
			while(r<q[i].r) addr(l,r+1),r++;
			while(l<q[i].l) dell(l,r),l++;
			while(r>q[i].r) delr(l,r),r--;
			res[q[i].id]=ans;
		}
		for(int i=1;i<=m;i++) printf("%lld\n",res[i]);
	}
	return 0;
 } 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值