@ hdu 5726
ST表+gcd()
数据大,不能暴力求,用线段树会超时,我们可以利用ST表求区间gcd;
至于第二问,对于以ai开头的区间来说,gcd随区间长度单调递减,藉此可在nlogn时间内利用二分求出每个gcd值出现次数,用map或hash存储即可
代码附下:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<map>
using namespace std;
typedef long long ll;
ll logs[111111];
ll n,q,t;
ll x,y;
ll cnt;
ll f[111111][20];
inline ll gcd(ll a,ll b)
{return b==0?a:gcd(b,a%b);}
ll query(ll x,ll y)
{
ll len=logs[y-x+1];
return gcd(f[x][len],f[y-(1<<len)+1][len]);
}
ll solve(ll a,ll tmp)
{
ll ans=-1,ret=0,now;
ll l=a,r=n;
while(l<=r)
{
ll mid=(l+r)>>1;
now=query(a,mid);
if(now>tmp)
l=mid+1;
if(now<=tmp)
r=mid-1;
if(now==tmp)
ans=mid;
}
if(ans!=-1)
now=query(a,ans);
else now=-1;
while(now==tmp&&ans<=n)
{
ret++;
ans++;
now=query(a,ans);
}
return ret;
}
int main()
{
for(ll i=1; i<=100000; ++i)
logs[i]=log2(i)+1e-6;
scanf("%lld",&t);
for(ll u=1;u<=t;u++)
{
scanf("%lld",&n,&q);
for(ll i=1;i<=n;i++)
scanf("%lld",&f[i][0]);
for(ll i=1;i<17;i++)
for(ll k=1;k<=n;k++)
{
if(k+(1<<i)-1>n) continue;
f[k][i]=gcd(f[k][i-1],f[k+(1<<(i-1))][i-1]);
}
map<ll,ll> rec;
for(ll i=1; i<=n; ++i)
{
ll g=f[i][0],j=i;
while(j<=n)
{
ll l=j,r=n;
while(l<r)
{
ll mid=l+r+1>>1;
if(query(i,mid)==g) l=mid;
else r=mid-1;
}
rec[g]+=(l-j+1);
j=l+1;
g=query(i,j);
}
}
printf("Case #%d:\n",u);
scanf("%lld",&q);
for(ll i=1;i<=q;i++)
{
scanf("%lld%lld",&x,&y);
cnt=0;
ll tmp=query(x,y);
printf("%lld %lld\n",tmp,rec[tmp]);
}
}
return 0;
}