LOJ#6083. 「美团 CodeM 资格赛」数码

版权声明:转载请注明 https://blog.csdn.net/qq_33831360/article/details/88564226

题目:

给定两个整数l和r,对于任意x,满足l≤x≤r,把x所有约数写下来。
对于每个写下来的数,只保留最高位的那个数码。求[1,9]中每个数码出现的次数.
1≤l≤r≤10^9

送分题?假的吧

首先考虑约数i会被统计r/i-(l-1)/i次,鉴于其形式相同,考虑怎么统计r/i

 喜闻乐见的分块

1、当i<sqrt(r)时只有sqrt(r)种情况,可以暴力统计

2、当i>=sqrt(r)时,r/i只有sqrt(r)种情况,可以把出现次数相同的约数统一统计

出现i次的约数是r/(i+1)+1到r/i,是连续的,那这些数有多少以1开头哪?其实可以统计该区间与{[1,1],[10,19],[100,199],[1000,1999]......}的重合部分的数量,这样就可以算了。

总复杂度O(sqrt(n)*log(n))

#include <iostream>
#include <cstdio>
#include <cmath>

typedef long long LL;
using namespace std;

LL cnt[11];

int f(int x) {
	while (x >= 10) x /= 10;
	return x; 
}

void Add(LL l,LL r,int v,int tp) {
	LL opr = tp, opl = tp;
    while(opl <= r) {
    	if(opr >= l) cnt[tp] += v*(min(r,opr)-max(l,opl)+1LL);
    	opl = opl*10;
    	opr = opr*10+9;
	} 
}

void calc(int r,int cas) {
	int lim = (int)sqrt(r);	
	for (int i = 1; i <= lim; i++)
	  for (int j = 1; j <= 9; j++)
	    Add(r/(i+1)+1,r/i,cas*i,j);
	for (int i = r/(lim+1); i >= 1; i--) cnt[f(i)] += cas*r/i;  
}

int main() {
	int l,r; cin >> l >> r;
	calc(r,1); calc(l-1,-1);
	for (int i = 1; i <= 9; i++) printf("%lld\n",cnt[i]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_33831360/article/details/88564226
今日推荐