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