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

本文探讨了如何通过因子查找优化区间贡献计算的问题,详细解释了如何利用因子关系来简化区间贡献的求解过程,提供了具体的算法实现和复杂度分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;
 
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值