题目大意:给定一个序列含有n个数,定义f(l,r) = l ~r序列中没有因子的数的个数 ,求n个数的序列中所有的连续子序列的f和。对1000000007取模
思路:定义数组l , r .l[i] , r[i] 分别表示第i个元素左边最近因子的位置和右边最近的因子的位置。则第i个数贡献的f值为(i - l[i]) * (r[i] - i )。
在求l 和r的过程中,肯定不能每个因子都去遍历着找,必然会超时。首先,一个数a的因子必然在sqrt(a)内能找到,所以只需要枚举从sqrt(a)到1所有的因子,找到最近的一个l和r
即可。然后就是如何尽快的找到因子所在的位置,刚才说了,肯定不能去遍历,不然必然超时,如何用元素来确定他的下标?就想到了STL里面的vector,可以开10000个vector,
就定义为vector<int > v[10000],然后输入a[i]时。只需要v[a[i]].push_back(i)即可,然后查找的时候遍历即可,其实和hash思想差不多。
另外强调一点,x是y的因子,则y/x也是y的因子,y/x比一定小于sqrt(y)!!
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <iomanip>
using namespace std;
#define MAXN 100005
#define MOD 1000000007
int a[MAXN] , l[MAXN] , r[MAXN];
int load[10005];
vector<int> v[10005];
vector<int>::iterator it;
int n;
void slove(int pos)
{
int num = (int)sqrt(a[pos]);
if(num * num != a[pos]) num ++;
int maxl = 0;
int minr = n + 1;
for(int i = num ; i > 0 ; i --)
{
if(a[pos] % i != 0) continue; //若不是因子,直接下一个
int res = a[pos] / i; //是因子 , 则要找到他的另一个因子,一起判断
for(int j = 0; j < v[i].size() ; j ++ )
{
if(v[i][j] < pos) maxl = max (maxl , v[i][j]);
if(v[i][j] > pos) minr = min (minr , v[i][j]);
}
for(int j = 0; j < v[res].size() ; j ++ )
{
if(v[res][j] < pos) maxl = max (maxl , v[res][j]);
if(v[res][j] > pos) minr = min (minr , v[res][j]);
}
}
l[pos] = maxl;
r[pos] = minr;
return ;
}
int main()
{
while(scanf("%d" , &n) != EOF)
{
memset(l , 0 , sizeof(l));
memset(r , 0 , sizeof(r));
for(int i = 1 ; i <= 10000 ; i ++ ) v[i].clear();
for(int i = 1 ; i <= n ; i ++ )
{
scanf("%d" , &a[i]);
v[a[i]].push_back(i); //记录每个数字的位置
}
for(int i = 1 ; i <= n ; i ++ )
{
slove(i); //找到l,r
}
int sum = 0;
for(int i = 1 ; i <= n ; i ++ )
{
sum = (sum + (i - l[i])*(r[i] - i) % MOD ) % MOD;
}
cout << sum << endl;
}
return 0;
}