51nod 冲刺题

进阶习题: 

http://www.51nod.com/Question/Index.html#questionId=1481数数字

http://www.51nod.com/Challenge/Problem.html#problemId=2151队列复原

https://www.51nod.com/Challenge/Problem.html#problemId=3593小明的数字表 vector-STL

http://www.51nod.com/Question/Index.html#questionId=8238小明爱排队

http://www.51nod.com/Question/Index.html#questionId=3217合法括号序列 V1

和为k的连续区间

位运算:

一个奇数次

授勋

区间xor

​​​​​​​

数学方面:

(斐波那契数列)

骨牌覆盖

ProjectEuler 2

跳房子

机器人走方格(dp初步)

机器人走方格 V5

​​​​​​​

质数篇章:

ProjectEuler 7

ProjectEuler 37

1到N的最小公倍数

分解质因数

ProjectEuler 12

取模运算:

n^n的末位数字

求递推序列的第N项

关于康托展开需要谈一下的:

康托展开

康托展开是一个全排列到一个自然数的双射,常用于构建哈希表时的空间压缩。 康托展开的实质是计算当前排列在所有由小到大全排列中的顺序,因此是可逆的。

康托展开运算

𝑋=𝑎𝑛(𝑛−1)!+𝑎𝑛−1(𝑛−2)!+...𝑎10!

其中  𝑎𝑖 为整数,并且满足  0≤ai<i,1≤i≤n 。

𝑎𝑖 表示原数的第  i 位在当前未出现的元素中排在第几。

例如:在  12345 的排列组合中,计算  34152 的康托展开值。

未出现且小于当前值的数 展开值
3 1,2=>𝑎5=21 𝑎5×4! 48
4 1,2=>𝑎4=21 𝑎4×3! 12
1 无  =>𝑎3=0 𝑎3×2! 0
5 2=>𝑎2=12 𝑎2×1! 1
2 无  =>𝑎1=0 𝑎1×0! 0

所以比  34152 小的组合有  61 个,即  34152 是排第  62 。

康托展开逆运算

康托展开是一个全排列到一个自然数的双射,因此是可逆的。即对于上述例子,在给出  61 可以算出起排列组合为  3415234152 。由上述的计算过程逆推回来,具体过程如下:

61/4!=2 余  13,说明,说明比首位小的数有 2 个,所以首位为 3 。

13/3!=2余  1,说明,说明在第二位之后小于第二位的数有 2 个,所以第二位为  4 。

1/2!=0余 1 ,说明,说明在第三位之后没有小于第三位的数,所以第三位为 1 。

用  1/1!=1 余 0 ,说明,说明在第四位之后小于第四位的数有  1 个,所以第四位为 5 。

最后一位自然就是剩下的数  2 。通过以上分析,所求排列组合为  34152。

数数字

统计一下 a*a*a ⋯ a*a*a(n个a) × b的结果里面有多少个数字d,a,b,d均为一位数。

样例解释:

3333333333*3=9999999999,里面有10个9。

输入格式

多组测试数据。 第一行有一个整数T,表示测试数据的数目。(1≤T≤5000) 接下来有T行,每一行表示一组测试数据,有4个整数a,b,d,n。 (1≤a,b≤9,0≤d≤9,1≤n≤10^9)

输出格式

对于每一组数据,输出一个整数占一行,表示答案。

输入样例

2
3 3 9 10
3 3 0 10

输出样例

10
0

 思路:这题 b 也只能是个位数,就很简单了

一些例子:22222 * 6 = 133332 (2*6=12, 1与2之间填充3)
22222 * 7 = 155554 (2*7=14, 1与4之间填充5)
22222 * 8 = 177776 (2*8=16, 1与6之间填充7)
22222 * 9 = 199998 (2*9=18, 1与8之间填充9)
33333 * 4 = 133332 (3*4 =12, 1与2之间填充3)

所以我们会发现:随着 n 增加,只有中间那个数字的个数会变多

n<=9 的时候暴力做就可以了;n>9的时候,暴力算一下 n=9 的时候中间那个数是多少,两边的数是多少,然后增加中间那个数的个数就行

官方题解代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int cnt(ll v, int d) {
    int ans = 0;
    while (v) {
        if (v % 10 == d)
            ans++;
        v /= 10;
    }
    return ans;
}
ll getNum(int n) {
    ll m = 1;
    for (int i = 1;i < n;i++)
        m = m * 10 + 1;
    return m;
}
int ssz() {
    int a, b, d, n, ct;
    ll v;
    cin >> a >> b >> d >> n;
    v = getNum(min(n, 11));
    v *= a * b;
    ct = cnt(v, d);
    if (n < 11)
        return ct;
    if (ct > 6)
        ct = n - 11 + ct;
    return ct;
}
int main() {
    int n;
    cin >> n;
    for (int i = 0;i < n;i++) {
        cout << ssz() << endl;
    }
}

 我的思路:

分为三种情况:
1.a * b < 10,那么所有的数不需要进位,如果a*b=d,那么d的个数就是n个,如果≠就是0个,这种情况最简单了
2.a * b >= 10,那么a*b就需要向前进位,注意这里要考虑两种情况:
(a * b)/10 + (a * b)%10 < 10,比如6666 * 2 = 13332,(6 * 2)/10 + (6 * 2)%10 = 3,也就是向前进位一次即可。
(a * b)/10 + (a * b)%10 >= 10,比如6666 * 8 = 53328,(6* 8)/10 + (6 * 8)%10 = 4 + 8 = 12,那这里又需要一次进位,也就是进位两次(不可能进位三次,因为最大也就是9+9=18,1向前进位,8落到原位,1 + 8 = 9 < 10)

找规律具体措施:

当n >= 3的时候,比如a=6,n=3,b=8,那么就是666*8,结果是5328,当n=4的时候,其他一样,结果是53328,那么就可以发现规律了,除了最左一位、最右一位,右边数第二位,除了这几个是没有规律的,中间的n-3项都是一样的,比如这里的:5 = (6*8)/10 + 1 --- 最左一位8 = (6*8)%10 --- 最右一位2 = ((6*8)/10 + (6*8)%10)%10 = (4+8)%10 --- 右边数第二位 3 = ((6*8)/10 + (6*8)%10)%10 + 1 = (4+8)%10+1 --- 中间的n-3项,后面的那个+1,就是((6*8)/10 + (6*8)%10)/10 = (4+8)/10 = 1
 

下面上代码!

#include<iostream>
#include<cstdio>
using namespace std;
 
int main()
{
	int t,a,b,d,n;
	cin >> t;
	while(t--)
	{
		cin >> a >> b >> d >> n;
		int ans = 0;
		if(a*b % 10 == d)
			ans++;
		if(n == 1 && a*b/10 == d && a*b >= 10)
			ans++;
		if(n >= 2)
		{
			if((a*b+a*b/10) % 10 == d)
				ans++;
			if((a*b + (a*b+a*b/10)/10) % 10 == d)
				ans += n-2;
			if((a*b + (a*b+a*b/10)/10) >= 10 && (a*b + (a*b+a*b/10)/10) /
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值