Part 1
难题部分:
Day 1 Moring
魔数
【题目描述】
小林和亮亮是好朋友。小林有一个幸运数字 a,亮亮有一个幸运数字 b。一
个数字称之为“幸运数”当且仅当它只由 a 和 b 组成。小林称一个数为“魔数”,
当且仅当这个数各数位之和为“幸运数”,且这个数字本身也是“幸运数”。
举个例子:小林的幸运数字为 2,亮亮的幸运数字为 6,那么 23 不是“幸运
数”,而 26、222 是“幸运数”。进一步,222 是“魔数”(因为 2+2+2=6),而 26
不是“魔数”(因为 2+6=8)。
亮亮想要知道,有多少个 n 位的“魔数”(一个 n 位数不包含前导 0),由于
这个数字会很大,所以亮亮只关心这个数模 1000000007(10^9+7,是一个质数)
的结果。
【输入格式】
只有一行,包含三个整数:a、b、n。意义见题目描述。
【输出格式】
只有一个整数,表示 n 位的“魔数”的个数模 1000000007 的结果。
【样例输入】
2 6 3
【样例输出】
1
【样例解释】
两个幸运数字分别为 2 和 6,则位数为 3 的“魔数”只有 222 一个。
【数据规模】
对于 30%的数据:1≤n≤5;
对于 60%的数据:1≤n≤100;
对于 100%的数据:1≤a<b≤9, 1≤n≤10^6。
这道题给了做够的暴力分,通过dfs逐位数枚举再判断数位和是否为幸运数便可骗到三十分,但是如果换个角度,只要知道了幸运数,便可通过方程算出a,b的个数,若设有x个a,则有(n - x)个b,现在的幸运数为k,则有方程\(ax + b(n - x) = k\),解得到\(x\) = \(\frac{k - b * n}{a - b}\),又因为让a,b随意排列等于先放一种球到盒子里,另一种就固定了。
注意到这道题值得学习的地方,首先是它的枚举幸运数的方式(我一开始就只想到dfs),我们可以知道幸运数的极大值为b * n,可以通过巧妙的\(- b + a\)使得b的数量减1,a的数量加1,循环变量i表示有多少个a在里面。还有再求同一底数的组合数时,用\(C_{n}^{i}\) = \(C_{n - 1}^{i}\) \(\times\) \(\frac{n - i + 1}{i}\)优化复杂度。
代码:
#include <bits/stdc++.h>
using namespace std;
int a,b,n;
const int Mod = 1e9 + 7;
const int MAXN = 1e7 + 5;
typedef long long ll;
long long sum;
long long ans;
long long C[MAXN];
bool check(long long k)
{
bool flag = true;
while(k)
{
if(k % 10 != a && k % 10 != b)
{
flag = false;
return flag;
}
k /= 10;
}
return flag;
}
long long ksm(long long x,long long y)
{
long long ans = 1;
while(y)
{
if(y & 1)ans = (ans * x) % Mod;
y >>= 1;
x = (x * x) % Mod;
}
return ans;
}
int main()
{
scanf("%d %d %d", &a, &b, &n);
C[0] = 1;
for(int i = 1;i <= n; i++)
C[i] = ((C[i - 1] * ksm(i,Mod - 2) % Mod * (n - i + 1)) ) % Mod;
sum = b * n;
if(check(sum))ans++;
for(int i = 1;i <= n; i++)
{
sum = sum + a - b;
if(check(sum))ans += C[i];
ans %= Mod;
}
printf("%lld", ans);
return 0;
}
Day 1 Night(高精度专题)
【高精度】e的n次方
问题描述
计算e的n次方,截断保留m位有效数字。
输入格式
只有一行n,m。
输出格式
只有一行,e的n次方的m位有效数字。
样例输入
样例输入1
1 10
样例输入2
5 10
样例输出
样例输出1
2.718281828
样例输出2
148.4131591
提示
n<=200,m<=10000
公式:\(e^n=\sum_{k=0}^\infty \frac{n^k}{k!}\)
本题思路比较明显,可以通过算增加量的方法计算e,可知增加的量为\(\frac{n}{i}\)(i为当前枚举到的k),因为有小数,只需将当前小数点位置记录,计算过程中小数点不会改变,将整个数乘10000就行了。
注意:增量是由上次计算增量的值相乘而得,并非记录答案的数。
代码(暂时只有80分,没有压位)
#include <bits/stdc++.h>
using namespace std;
#define R register
typedef long long ll;
const int MAXN = 1e5 + 5;
struct node
{
short int a[MAXN],len;
inline void Clear()
{
memset(a,0,sizeof(a));
len = 0;
}
inline void read()
{
string s;
cin >> s;
len = s.size();
for(R int i = 1;i <= s.size(); i++)
a[i] = s[len - i] - '0';
}
inline node operator / (const ll &A)const
{
ll sum = 0;
node c;
c.Clear();
c.len = len;
for(R int i = len;i >= 1; i--)
{
sum = sum * 10 + a[i];
if(sum >= A)
{
c.a[i] = sum / A;
sum %= A;
}
}
while(c.a[c.len] == 0 && c.len > 1)
c.len--;
return c;
}
inline node operator * (const ll &A)const
{
node c;
c.Clear();
c.len = len;
ll sum = 0;
for(R int i = 1;i <= len; i++)
{
c.a[i] += a[i] * A;
c.a[i + 1] += c.a[i] / 10;
c.a[i] %= 10;
}
while(c.a[c.len + 1] > 0)
{
c.len++;
c.a[c.len + 1] += c.a[c.len] / 10;
c.a[c.len] %= 10;
}
return c;
}
inline void write()
{
for(R int i = len;i >= 1; i--)
printf("%d", a[i]);
}
inline node operator + (const node &A)const
{
node ans;
ans.Clear();
ans.len = max(A.len,len);
for(R int i = 1; i <= ans.len; i++)
{
ans.a[i] += a[i] + A.a[i];
while(ans.a[i] >= 10)
{
ans.a[i] -= 10;
ans.a[i + 1]++;
}
}
while(ans.a[ans.len + 1] > 0)
{
ans.len++;
}
return ans;
}
inline bool cmpy(const node &A)
{
if(len != A.len)return false;
for(int i = len;i >= 0; i--)
if(a[i] != A.a[i])
return false;
return true;
}
};
int main()
{
int m;
ll n;
scanf("%lld %d", &n, &m);
node s;
s.len = 5000;
s.a[5000] = 1;
node Add,k,ans;
ans.a[5000] = 1;
ans.len = 1;
for(R ll i = 1;; i++)
{
k = ans;
s = s * n / i;
ans = ans + s;
if(ans.cmpy(k) == true)break;
}
for(R int i = ans.len;i >= ans.len - m + 1; i--)
{
printf("%d", ans.a[i]);
if(i == 5000)
printf(".");
}
return 0;
}