http://acm.hdu.edu.cn/showproblem.php?pid=5288
题意:
对一个区间【l,r】,我们看有多少个ai,满足ai%区间内任何数(除了ai) 都不为零,也即整除
如果有x个ai,则这个区间的贡献为x
给n个数数组,求所有区间的贡献和。
思路:直接求每个区间的贡献并不好求,我们可以转化为【求每个数ai对答案的贡献】。
对ai,我们找到分别在左边和右边离ai最近的一个因子y1,y2
那么显然在【y1+1,y2-1】内的所有包含ai的区间里,ai都满足 【不能整除区间内任何数】
这个数就是 (i-y1) * (y2-i);
那么答案只要累计每个数ai的贡献便可
那么我们怎么求离ai左右最近的因子呢, 先把所有数a[i]存到一个vector【a[i]】(下标升序)
那么每次对ai处理时,我们先枚举ai的所有因子j
然后在vector【j】里二分查找并更新【离ai左右最近的因子的位置】
因此复杂度是n*sqrt(n)* 【k】 (因为vector里面的数一般都是很少的,所以这个查找近乎为常数的量级
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <iostream>
using namespace std;
const double pi=acos(-1.0);
double eps=0.000001;
__int64 min(__int64 a,__int64 b)
{return a<b?a:b;}
__int64 max(__int64 a,__int64 b)
{return a>b?a:b;}
int tm[100005];
vector<int>mp[10005],sb; //hash
__int64 mod=1e9+7;
int main()
{
int n,i,j;
while(scanf("%d",&n)!=EOF)
{
for (i=1;i<=10005;i++) mp[i].clear();
for (i=1;i<=n;i++)
{
scanf("%d",&tm[i]);
mp[tm[i]].push_back(i);
}
__int64 ans=0;
for (i=1;i<=n;i++)
{
__int64 minn=0;
__int64 maxx=n+1;
for (j=1;j*j<=tm[i];j++) //枚举因子
{
if (tm[i]%j==0)
{
int tmp=j;
int it=upper_bound(mp[tmp].begin(),mp[tmp].end(),i)-mp[tmp].begin(); //二分找到第一个在i右边的值
if (it!=mp[tmp].size())
maxx=min(maxx,mp[tmp][it]);
it=lower_bound(mp[tmp].begin(),mp[tmp].end(),i)-mp[tmp].begin(); //二分找到第一个在i左边的值
it--;
if (it>=0)
minn=max(minn,mp[tmp][it]);
if (j==tm[i]/j)continue;
tmp=tm[i]/j;
it=upper_bound(mp[tmp].begin(),mp[tmp].end(),i)-mp[tmp].begin();
if (it!=mp[tmp].size())
maxx=min(maxx,mp[tmp][it]);
it=lower_bound(mp[tmp].begin(),mp[tmp].end(),i)-mp[tmp].begin();
it--;
if (it>=0)
minn=max(minn,mp[tmp][it]);
}
}
ans+=(maxx-i)*(i-minn)%mod; //累加tm[i]的贡献
ans%=mod;
}
printf("%I64d\n",ans%mod);
}
return 0;
}