1. 程式人生 > >codeforces 895/B sort+二分

codeforces 895/B sort+二分

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