您的位置:首页 > 产品设计 > UI/UE

HDU - 5288-OO’s Sequence-数学+分类再二分+枚举+contribution costing

2016-04-02 17:03 411 查看
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;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: