codeforces 895/B sort+二分

解析一道算法竞赛题目,涉及有序对计数与特定条件筛选。通过排序与二分搜索技巧优化解决方案。

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

B. XK Segments
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

While Vasya finished eating his piece of pizza, the lesson has already started. For being late for the lesson, the teacher suggested Vasya to solve one interesting problem. Vasya has an array a and integer x. He should find the number of different ordered pairs of indexes (i, j) such that ai ≤ aj and there are exactly k integers y such that ai ≤ y ≤ aj and y is divisible by x.

In this problem it is meant that pair (i, j) is equal to (j, i) only if i is equal to j. For example pair (1, 2) is not the same as (2, 1).

Input

The first line contains 3 integers n, x, k (1 ≤ n ≤ 105, 1 ≤ x ≤ 109, 0 ≤ k ≤ 109), where n is the size of the array a and x and k are numbers from the statement.

The second line contains n integers ai (1 ≤ ai ≤ 109) — the elements of the array a.

Output

Print one integer — the answer to the problem.

Examples
input
4 2 1
1 3 5 7
output
3
input
4 2 0
5 3 1 7
output
4
input
5 3 1
3 3 3 3 3
output
25
Note

In first sample there are only three suitable pairs of indexes — (1, 2), (2, 3), (3, 4).

In second sample there are four suitable pairs of indexes(1, 1), (2, 2), (3, 3), (4, 4).

In third sample every pair (i, j) is suitable, so the answer is 5 * 5 = 25.


题目大致的意思是说给你n个数 ai ....aj 其中1<=i<=j<=n

然后有一个x 和k 你要寻找所有的序列 使得

这个序列中的从第一个元素到最后一个元素(包括这两个)之间恰好有k个可以被x整除

看用例第一个用例就是让我们找到所有的序列满足他们之间最多被2整除的数有一个

那么1,3 3,5 5,7 是这样的唯一三组数 对应的下标为 (1, 2), (2, 3), (3, 4)

那如果是 (1,3)呢  这样的话 1,2,3,4,5 中间2,4都可以被2整除 就不满足只有一个的情况了。

最后要你输出的是所有这样的序列数之和。

解题思路:

可以看出,如果打乱原序列是不会改变所得结果的个数的,其实这个证明起来也不难,但是我觉得没有必要把时间浪费在这里

当时就这么做了,其实也是怀着猜的心态,但是后来感觉还好。因为他说(i,j) 和(j,i)不是一对的, 所以如果你(i,j)原来是满足的话

那么如果排序后打乱顺序,他们在的位置是(i2,j2) 或者(j2,i2)一定有且唯一有一个满足条件。具体的话有兴趣的可以自己尝试一下,

重点是sort之后可以用二分 可以缩短时间复杂度,故想到排序这样。

那么我们对这组数进行排序之后 开始用二分查找,怎么查找呢?

我分了几类讨论,在代码中有注释,如果想自己再试下我先说个思路 

被 x整数的有k个 那么对于 第a[i]的数来说 可以允许的数大约是k*x+a[i附近(当然要考虑余数的情况)

可以允许最大的数确定了之后允许的最小的数也就确定 max-(x-1)  就是

比如你是要被5整数  你现在是1 然后 只允许有一个 所以是 5-9 都可以 10 就又可以被整除了 不能要

而如果是4的话 一个都没有 也不可以

照着这个思路程序如下:

#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;
typedef long long ll;

struct node
{
	ll num,val; 
};
node a[100005];

bool cmp(const node&a,const node &b)
{
	if(a.val!=b.val) return a.val<b.val;
	else return a.num<b.num;
}
//两种二分方式 
inline ll bis1(ll,ll);
inline ll bis2(ll,ll);
//key1 key2 分别代表当前数允许的最小值和最大值 
ll n,x,k,key1,key2;
int main()
{
	scanf("%lld%lld%lld",&n,&x,&k);
	for(int i=0;i<n;i++)
	{
		scanf("%lld",&a[i].val);
		a[i].num=i+1; 
	}
	sort(a,a+n,cmp);
	
	ll ans=0;
	ll len;
	ll l,r;
	for(int i=0;i<n;i++)
	{
		//分了四类情况
		//第一类是当前数整除但是只有0个整数 直接continue
		//第二类是当前数整除 k不等于0 修正key1
		//第三类是k==0 修正key2 
		if(a[i].val%x==0&&k==0)  continue;  
		else if(a[i].val%x==0&&k!=0) 
		{
			key1=a[i].val+x*(k-1);
			key2=key1+x-1;
		}
		else if(a[i].val%x!=0&&k==0)
		{
			key1=a[i].val;
			key2=key1+x-1-a[i].val%x;
		}
		else
		{
			key1=a[i].val+k*x-a[i].val%x;
			key2=key1+x-1;
		}
		//考虑两种情况 一种是查最小的时候 要优先选左侧的  查最大的优先右侧 
		//就是改变等号归哪边的事 
		l=bis1(0,n-1);
		r=bis2(0,n-1);
		if(a[r].val>=key1)
		ans+=r-l+1;
	}
	
	printf("%lld\n",ans);
	return 0;
} 

inline ll bis1(ll l,ll r)
{
	if(r<=l+1) 
	{
		if(a[l].val>=key1) return l;
		else return r;

	}
	ll mid=(l+r)/2;
	if(key1<a[mid].val) bis1(mid,r);
	else bis1(l,mid);	
}

inline ll bis2(ll l,ll r)
{
	if(r<=l+1) 
	{
		if(a[r].val<=key2) return r;
		else return l;	
	}
	ll mid=(l+r)/2;
	if(key2>=a[mid].val) bis2(mid,r);
	else bis2(l,mid);	
}
我承认有点麻烦..不过最后一A还是很开心的啊hh

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值