小乐乐与二段数
题目链接
暴力做法会导致超时,所以不能使用暴力做法
#include <iostream>
#include <cmath>
#include <vector>
#include <set>
using namespace std;
typedef long long LL;
int isdoubleNum(LL n) {
set<int> s;
int flag = 0;
int c = n % 10;
while (n)
{
int a = n % 10;
if (s.size() == 1)
{
for (int i : s)
if (i != a)
{
s.insert(a);
flag = 1;
}
}
if (s.size() == 2 && c == a)
return 0;
s.insert(a);
n /= 10;
}
if (s.size() == 2)
return 1;
return 0;
}
int main() {
LL n, m;
while (cin >> n) {
if (n == 0)
break;
for (LL i = n; ; i += n) {
if (isdoubleNum(i))
{
m = i;
break;
}
}
printf("%lld: %lld\n", n, m);
}
return 0;
}
预先计算好两个数列。
数列1:a[1]=1,a[i+1]=(a[i]*10+1) mod N,i = 1,2,3,… a[i]就是连续i个1除以N的余数。
数列2:b[0]=1,b[i+1]=b[i]*10 mod N,i = 0,1,2,3,… b[i]就是10的i次方除以N的余数。
有了这两个数列,只需用O(1)的时间就可以计算任意二段数除以N的余数。假设二段数是由m个s和n个t组成,二段数m,s,n,t除以N的余数等于(a[m]*b[n]*s+a[n]*t) mod N。
接下来只要枚举m,s,n,t就可以了。按照(m+n)的值从小到大枚举,(m+n)确定后枚举m,则n可以直接计算出来,不需要枚举。m和n确定之后枚举s和t。一旦找到解,后面的(m+n)值就不需要继续枚举下去了。为了减少不必要的枚举,可以先进行判断。例如,N是10的倍数时,t只能取0。
AC CODE
:
#include <iostream>
using namespace std;
const int maxn = 10010; //表示最大的位数长度,定义为const,不可变的固定大小
int a[maxn], b[maxn];
int m, total, s, t, aptotal, apm, aps, apt, k;//先声明要用到的变量,记录符合要求的最小二段数
int n;
bool ck()//返回为bool类型的函数,检测二段数是否大于n
{
int p, r;
if (total > 5)//剪枝
return 1;
p = s;
r = t;
for (int q = 0; q < m; q++)
{
p = p * 10 + s;
}
for (int q = 0; q < total - m; q++)
p = p * 10;
for (int q = 1; q < total - m; q++)
r = r * 10 + t;
return p + r > n;
}
int main()
{
while (cin >> n, n)
{
printf("%d: ", n);
if (n == 1) {
puts("10");
continue;
}
a[0] = 1;
b[0] = 1;
for (int i = 1; i < 9999; i++)//初始化数组
a[i] = (a[i - 1] * 10 + 1) % n;
for (int i = 1; i < 999; i++)
b[i] = b[i - 1] * 10 % n;
for (total = 1, aps = 0; total < 9999; total++) {
k = 0;
if ((n % 10 == 0 || n % 25 == 0) && total > 11)
k = total - 11;
for (m = k; m < total; m++)
for (s = 1; s < 10; s++)
for (t = 0; t < (n % 10 ? 10 : 1); t++)
if (t != s && (((long long)a[m]) * b[total - m] * s + a[total - m - 1] * t) % n == 0
&& ck() && (!aps || s < aps)) {
aptotal = total;
apm = m;
aps = s;
apt = t;
}
if (aps)
break;
}
for (int x = 0; x < apm + 1; x++)
cout << aps;
for (int x = 0; x < aptotal - apm; x++)
cout << apt;
cout << endl;
}
return 0;
}