【数论】Concatenated Multiples【codeforces-Round #506-div3-D】

题意:

     给定n和k,表示一共n个数字,问将其中两个数字拼接起来能够被 k 整除的组合有多少种。比如:

6 11

45 1 10 12 11 7

     答案就是7,一共是451、4510、110、1045、1012、121、1210可以整除11。

思路:

     看到题目以后,先想的是两个数相加整除另一个数的条件是什么,然后一开始想是不是两个数都能整除一个数,这两个数再相加 才能整除这个数,然后当然显然不是。

     接着再仔细想了想,拿了两个数实验了一下,发现这两个数 /k之后的小数部分相加刚好为1,于是便顺理成章地想到了两个数的%k的余数相加为k或者为0即能整除k。

     因此再读入数的时候,就将不同位数的数%k的余数放到不同的数组中,一共10个数组,再记录每个数组的元素个数。

    再对每个数进行遍历,比如45,就将45*10再*10,每次乘都求出该数%k的值,然后再在对应的区间内进行二分查找相应的余数,然后最后就是答案了。

注意:

     可能一个数会和自己拼接,因此要注意将这部分的情况减去,还有就是可能会爆long long,我开的是unsigned long long。

最后感叹一下,虽然想出了正解,而且打出了代码,但是5、6个bug,最后还是没调出来...还要继续努力啊!!

代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define rep(i,a,b) for(ll i = a;i <= b;i++)
using namespace std;
const int N = 2*1e5+100;
typedef unsigned long long ll;

ll a[15][N];
int t[15];
ll num[N];
ll k,n;

int main()
{
	while(~scanf("%I64u%I64u",&n,&k))
	{
		memset(t,0,sizeof t);
		rep(i,1,n){
			scanf("%I64u",&num[i]);
			int cnt = 1;
			ll h = 10;
			while(h <= num[i]){h*=10; cnt++;}
			a[cnt][++t[cnt]] = (num[i]%k);
		}
		rep(i,1,10)
		{
			sort(a[i]+1,a[i]+1+t[i]);
		}
		ll ans = 0;
		rep(i,1,n)
		{
			ll x = 1;
			int jud = 1;
			rep(j,1,10)
			{
				x *= 10;
				ll hm = num[i]*x;
				ll tp = hm%k;
				if(num[i] < x && jud == 1)
				{
					if((tp+num[i]%k) == k || (tp == 0 && (num[i]%k) == 0)){
						ans--;
					} 
					jud = 0;
				}
				ll arc = k-tp;
				if(tp == 0) arc = 0;
				int l = lower_bound(a[j]+1,a[j]+t[j]+1 , arc)- a[j] ;
				int r = upper_bound(a[j]+1,a[j]+t[j]+1 , arc) - a[j] ;
				r--;
				int hmm = r-l+1;
				hmm = max(0,hmm);
				ans += hmm;
			}
		}
		printf("%I64u\n",ans);
	}
	return 0;
}

/*
3 1000000000
1000000000 1000000000 1000000000
*/

猜你喜欢

转载自blog.csdn.net/qq_41552508/article/details/82078216
今日推荐