SPOJ - TBATTLE

http://www.spoj.com/problems/TBATTLE/en/
题意:给一个数字n,后面有n个数,求出最小能被n整除的区间,如果有多个解就取左边的。

思路:先对n进行分解质因数,然后对后面n个数分解质因数,用一个二位数组zu[i][j]以前缀和的方式存不同质因数的数量(j为n的质因数种类)然后用取尺法去比较每个区间和n的质因数就可以了

#include<iostream>
#include<stdio.h>
#include<cstring>
#define inf 999999999
using namespace std;
int su[100005],nn[30],snum,n,tmp[30],zu[100005][30];
bool judge[100005];
void init()//用筛法预处理除所有的素数
{
	snum=0;
	for(int i=2;i<=100000;i++)
	{
		if(!judge[i])su[++snum]=i;
		for(int j=2;i*j<=100000;j++)
		{
			judge[i*j]=true;
		}
	}
}
int main()
{
	int nmax;
	init();
	while(scanf("%d",&n)!=EOF)
	{
		memset(nn,0,sizeof(nn));
		memset(zu,0,sizeof(zu));
		nmax=0;
		int tp=n;
		for(int i=1;su[i]*su[i]<=tp&&i<=snum;i++)
		{
			if(tp%su[i]==0)
			{
				tmp[++nmax]=su[i];//tmp存n的质因数,n存每种质因数的数量
				nn[nmax]=0;
			}
			while(tp%su[i]==0)
			{
				nn[nmax]++;//符合条件,su[i]这个种类数量++
				tp/=su[i];
			}
		}
		if(tp>1)
		{
			tmp[++nmax]=tp;
			nn[nmax]++;
		}
		int x;
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&x);
			for(int j=1;j<=nmax;j++)
			zu[i][j]=zu[i-1][j];//前缀和
			for(int j=1;j<=nmax;j++)
			{
				while(x%tmp[j]==0)//对于第i个数,处理它的质因数,拿它跟n的质因数去比即可,别的不用管
				{
					zu[i][j]++;
					x/=tmp[j];
				}
			}
		}
		int l,r,minn=inf,j=0;
		for(int i=1;i<=n;i++)
		{
			for(;j<i;j++)//取尺法改变右端时j要保持不变
			{
				bool flag=true;
				for(int k=1;k<=nmax;k++)
				{
					if(zu[i][k]-zu[j][k]<nn[k])
					{
						flag=false;
						break;
					}
				}
				if(!flag)break;
				if(i-j+1<minn)
				{
					l=j,r=i-1;
					minn=r-l+1;
				}				
			}
		}
		if(minn!=inf)
		printf("%d %d\n",l,r);
		else
		printf("-1\n");
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40942372/article/details/80011841
今日推荐