(多校第一场1004)HDU5726 GCD(区间GCD查询+)

题意:给出n个数,区间为(1,n),查询区间(l,r)的GCD,以及区间(1, n)有多少子区间的GCD等于区间(l,r)的GCD。

做法:使用线段树,或RMQ维护区间GCD,并且要预处理出所有可能出现的GCD值 的区间数量。针对固定左端点,一直向右GCD阶梯状减小的特性,大概有两种做法。

1)对于某一固定起点(以选定左端点L=i为例),从最右端R开始,二分枚举GCD突变位置pos,这一段阶梯的长度就是R-pos+1,定义INTERGCD(L,R)=区间(L,R)的GCD,则GCD值等于 INTERGCD(L,R)的区间数量就增加R-pos+1。

对于左端点i:令L=i,R=n。

a、l=L,r=R。

b、m=(l+r)/2,如果INTERGCD(L,m)<=INTERGCD(L,R),r=m;否则l=m+1;如果l==r,则阶梯突变位置就是l或r,记录pos=l,转c;否则重复b;

c、阶梯长度就是pos-R+1,则_map[INTERGCD(L,R)]+=pos-R+1;令R=pos-1,如果R>=L,转a,计算下一个阶梯;否则,以i为左端点的阶梯就计算完了。


2)以arr=(1,2,4,6,7)这列数为例,如图,数列下面是以某个i为端点的所有的GCD值,_tmp和_tmpnext是值为的map,_tmp记录当前列,各个GCD值的数量,_tmpnext记录下一行的_tmp值,_tmpnext由_tmp推得;对于第i-1列的_tmp的每一个元素iter(指针),_tmpnext[GCD(arr[i],iter->first)]+=iter->second。图中给出了两列所对应的_tmp(_tmpnext)的值,(可能第四列到第五列理解更清晰)。

                                                                                   (多校第一场1004)HDU5726 GCD(区间GCD查询+)_第1张图片



对于第一种写法,因为在预处理的时候要询问INTERGCD(l,r),所以用线段树,预处理的时间复杂度就成了O(n(logn)^3),就会超时。用RMQ,O(1)的查询就可以了。第二种写法在处理的时候不需要询问INTERGCD,就无所谓了。

附上两种写法:calcu_interval是第一种写法,progress是第二种。

RMQ

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define maxn 110000
#define LL long long
using namespace std;

int arr[maxn];
int _gcd[maxn][30];
map_map;

int _query(int l,int r)
{
	int len=r-l+1;
	int temp=(int)log2(double(len));
	return __gcd(_gcd[l][temp],_gcd[r-(1<_tmp,_tmpnext;
map::iterator iter;
void progress(int n)
{
	_map.clear();
	_tmp.clear();
	for(int i=1;i<=n;++i)
	{
		_tmpnext.clear();
		_tmpnext[arr[i]]++;
		for(iter=_tmp.begin();iter!=_tmp.end();iter++)
		{
			int now=__gcd(arr[i],iter->first);
			_tmpnext[now]+=iter->second;
		}
		for(iter=_tmpnext.begin();iter!=_tmpnext.end();iter++)
		{
			_map[iter->first]+=iter->second;
		}
		_tmp=_tmpnext;
	}
}

int main()
{
	int T,I=1;
	scanf("%d",&T);
	while(T--)
	{
		int n;
		scanf("%d",&n);
		for(int i=1;i<=n;++i)
		{
			scanf("%d",&arr[i]);
			_gcd[i][0]=arr[i];
		}
		int nl=(int)log2(double(n));
		for(int j=1;j<=nl;++j)
		{
			for(int i=1;i<=n;++i)
			{
				if(i+(1<<(j-1))<=n)
				{
					_gcd[i][j]=__gcd(_gcd[i][j-1],_gcd[i+(1<<(j-1))][j-1]);
				}
			}
		}
//		calcu_interval(n);
		progress(n);
		int m;
		scanf("%d",&m);
		printf("Case #%d:\n",I++);
		for(int i=0;i


线段树

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define maxn 110000
#define LL long long
using namespace std;

int arr[maxn];
struct seg
{
    int l,r;
    int gcd;
    seg *left,*right;
};
struct node
{
    int v,w;
};
vectorhead[maxn];
map_map;
seg  *build(int l,int r)
{
    int m=(l+r)/2;
    seg *rt=new seg;
    rt->l=l;
    rt->r=r;
    if(l==r)
    {
        rt->left=rt->right=NULL;
        rt->gcd=arr[l];
        return rt;
    }
    rt->left=build(l,m);
    rt->right=build(m+1,r);
    rt->gcd=__gcd(rt->left->gcd,rt->right->gcd);
    return rt;
}
int query(seg *rt,int ql,int qr)
{
    if(ql<=rt->l && rt->r<=qr)
    {
        return rt->gcd;
    }
    int l=rt->l;
    int r=rt->r;
    int m=(l+r)/2;
    int gcd=0;
    if(ql<=m)
        gcd=query(rt->left,ql,qr);
    if(qr>m)
        gcd=(gcd==0)?query(rt->right,ql,qr):__gcd(gcd,query(rt->right,ql,qr));
    return gcd;
}

map_tmp,_tmpnext;
map::iterator iter;
void progress(int n)
{
	_map.clear();
	_tmp.clear();
	for(int i=0;ifirst);
			_tmpnext[now]+=iter->second;
		}
		for(iter=_tmpnext.begin();iter!=_tmpnext.end();iter++)
		{
			_map[iter->first]+=iter->second;
		}
		_tmp=_tmpnext;
	}
}
void del(seg *rt)
{
    if(rt!=NULL)
    {
        del(rt->left);
        del(rt->right);
        delete rt;
    }
}
int main()
{
    int T,I=1;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        for(int i=0;i


你可能感兴趣的:(2016多校赛,二分,线段树)