题目大意:
给定长度为n(n<=
105
)的整数序列(a<=
104
),求出所有子区间中不被区间内其他数整除的数的个数和,结果取余
109+7
;
思路:
一个个枚举区间肯定行不通,可以根据每个整数对结果的贡献求和;
对于第i个数
ai
,找出
ai
左右第一个被整除的边界l、r,则
ai
的贡献为(i-l)*(r-i);
利用标记数组可以在sqrt(a)内找出左右边界,因此总复杂度为O(n*sqrt(a))。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int mod = 1e9+7;
const int MAXN = 1e5+10;
int a[MAXN], l[MAXN], r[MAXN];
int vis[MAXN];
int main(int argc, char const *argv[])
{
int n;
while(~scanf("%d", &n))
{
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]);
memset(vis, 0, sizeof(vis));
for(int i = 1; i <= n; i++)
{
l[i] = 0;
for(int j = 1; j * j <= a[i]; j++)
{
if(a[i] % j)
continue;
if(vis[j])
l[i] = max(l[i], vis[j]);
if(vis[a[i]/j])
l[i] = max(l[i], vis[a[i]/j]);
}
vis[a[i]] = i;
}
memset(vis, 0, sizeof(vis));
for(int i = n; i >= 1; i--)
{
r[i] = n + 1;
for(int j = 1; j * j <= a[i]; j++)
{
if(a[i] % j)
continue;
if(vis[j])
r[i] = min(r[i], vis[j]);
if(vis[a[i]/j])
r[i] = min(r[i], vis[a[i]/j]);
}
vis[a[i]] = i;
}
long long ans = 0;
for(int i = 1; i <= n; i++)
{
ans += (i - l[i]) * (r[i] - i);
ans %= mod;
}
printf("%I64d\n", ans);
}
return 0;
}