JZOJ 4307. 【NOIP2015模拟11.3晚】喝喝喝

JZOJ 4307. 【NOIP2015模拟11.3晚】喝喝喝

题目

Description

在这里插入图片描述

Input

在这里插入图片描述

Output

在这里插入图片描述

Sample Input

3 2
5 3 1

Sample Output

4

Data Constraint

在这里插入图片描述

题解

首先看一条显而易见的性质,若满足 x m o d    y = k x \mod y=k ,则 y x k y|x-k ,且 y > k y>k
为了求答案方便,每次循环到 i i 时,加上以 i i 结尾的满足条件子数组的个数。
怎么求?
我们不难发现,假设当前以 i i 结尾的满足条件子数组的开头最小可到 x x ,则以 i + 1 i+1 结尾的满足条件子数组的开头最小只能到 x x ,否则其中必包含“坏对”。
则我们设一个指针 l a s t last ,表示当前满足条件子数组的开头最小为 l a s t last l a s t last 是满足递增的。
考虑如何将 l a s t last 后移。
找到 a [ i ] a[i] 前最后一个 a [ l ] a[l] 满足 ( a [ l ] , a [ i ] ) (a[l],a[i]) 是一个坏对,也就是 a [ i ] a [ l ] k a[i]|a[l]-k
f [ i ] = l f[i]=l ,表示 i a [ l ] k i|a[l]-k ,且没有满足条件的更小的 l l ,也就是最后一个满足 i a [ l ] k i|a[l]-k l l
更新 l a s t last 时,判断 f [ a [ i ] ] f[a[i]] l a s t last 的大小关系,取较大值。
更新 f f 数组时,用 a [ i ] \sqrt{a[i]} 的时间,将所有的 f [ j ] = i f[j]=i j a [ i ] k j|a[i]-k )。
又有一个问题, ( 2 , 5 ) (2,5) 也是一个“坏对”,也就是 a [ x ] = k a[x]=k ,但用这种方法判断不出。
所以再用一个变量维护最后一个出现 a [ l ] = k a[l]=k 的位置。

代码

#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int f[100010],a[100010];
int main()
{
	int n,k,i,j,last=0,p=0;
	long long ans=0;
	scanf("%d%d",&n,&k);
	for(i=1;i<=n;i++) scanf("%d",&a[i]);
	for(i=1;i<=n;i++)
	{
		if(a[i]>k&&f[a[i]]>last) last=f[a[i]];
		
		if(a[i]>k&&p>last) last=p;
		ans+=i-last;
		int t=a[i]-k;
		if(a[i]==k) p=i;
		for(j=1;j<=floor(sqrt(t));j++) if(t%j==0) f[j]=f[t/j]=i;
	}
	printf("%lld",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39565901/article/details/82950801