牛客周赛Round39——小红不想做莫比乌斯反演杜教筛求因子和的前缀和

目录

题目描述

输入描述:

输出描述:

输入

输出

说明

思路

我直接疯狂之暴力

 代码实现

我是大聪明之我爱数学

        代码实现:

总结


链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

题目描述

        小红来到了一家蛋糕店,蛋糕店中售卖了若干种不同的长方体蛋糕,具体来讲,蛋糕店中售卖若干种形状为横向长度不大于nnn,纵向长度不大于mmm,高度不大于ppp个单位的蛋糕。每个蛋糕的表面要裹上奶油,也就是说,除了底面以外,其余5个面都需要裹奶油。我们不妨定义:奶油消耗量为暴露在空气中的5个面的面积之和。


        我们定义两种蛋糕是不同的,当且仅当两个蛋糕的横向或者纵向长度或高度不同。即分别定义蛋糕横向的长度为iii,纵向的长度为jjj,高度为kkk,则可以用三元组(i,j,k)(i,j,k)(i,j,k)表示蛋糕种类的唯一性。

        现在小红希望你求出,共有多少种不同的奶油消耗量为xxx的蛋糕?

输入描述:

第一行输入四个正整数n,m,p,xn,m,p,xn,m,p,x,用空格隔开。
1≤n,m,p≤30001\leq n,m,p \leq 30001≤n,m,p≤3000
1≤x≤1071\leq x \leq 10^71≤x≤107

输出描述:

消耗量为xxx的蛋糕的种类数。

示例1

输入

2 2 2 8

输出

2

说明

        如下图,共有以下两种蛋糕的奶油消耗量为8。


思路

我直接疯狂之暴力

        一开始肯定是直接三层for循环遍历三条边的全部长度获取全部的可能来写出这道题嘛。直接暴力的代码如下:

 代码实现

import java.util.*;

public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        //  横向长度
        int n = sc.nextInt();
        // 纵向长度
        int m = sc.nextInt();
        // 高度
        int p = sc.nextInt();
        // 面积
        int x = sc.nextInt();
        
        int res = 0;
        // 横向
		for (int i = 1; i <= n; i++) {
			// 纵向
			for (int j = 1; j <= m; j++) {
				// 高度
				for(int k = 1; k <= p; k++){
                    // 判断面积是否为x
                    if(2*i*k+2*j*k+j*k == x){
                        res++;
                    }
                }
			}
		}
		System.out.println(res);

	}
}

        但是这种我试过了,给你的测试用例当然是对的,但是只能通过20%的题目,其他的测试用例AC不了。因为他的   n,m,p \leq 3000 ,3000*3000*3000是多少,所以这个时间复杂度为

O(n^{3})的算法过不了一点。


我是大聪明之我爱数学

        有没有一个方法可以降低一层或者两层for循环呢?

        答案当然是存在的,我们来思考,n,m,p分别是横向,纵向和高度,x的面积可以由它们表示如下公式:

2np+2mp+nm=x

        那么如果我们想降低一层for循环,是不是就需要将其中一个未知量用其他的来代替,让他只有两个未知量,那我们可以提取一下 2p得到如下的公式:

2p(n+m) + nm = x

        是不是就可以把p的表达式求解出来如下公式:

p=\frac{x-nm}{2(n+m)}

        但是你如果把这个p带进原式你就会发现上面都没干,变成了x=x,那我们求解出这个答案干嘛。

        重点来了,这里我们得到了p的答案,x是固定的,那么 n和m我们可以使用两层for来遍历寻找,然后是不是就可以每次找到一个 p ,我们直接判断这个p是不是在题目中给的p的范围不就完美解决了吗?哈哈哈哈哈哈拿下!!!

        但是还是有坑,因为语言的问题,你之前输入的都是整数,如果遇到例如 \frac{7}{4} = 1,这种情况如果还恰好满足你的范围,那就出事了,所以我们应该排除小数的情况,只保留答案刚好是整数的情况,因此我们还需要增添一个 p 是整数的判断。

        代码实现:

import java.util.*;

public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        //  横向长度
        int n = sc.nextInt();
        // 纵向长度
        int m = sc.nextInt();
        // 高度
        int p = sc.nextInt();
        // 面积
        int x = sc.nextInt();
        
        int res = 0;
        // 横向
		for (int i = 1; i <= n; i++) {
			// 纵向
			for (int j = 1; j <= m; j++) {
				// 高度
				double k = (x - i*j)*1.0/(2*(i+j));
                // 判断是否为整数且在范围中
				if( k % 1 == 0 && k <= p && k > 0) {
					res++;
				}
			}
		}
		System.out.println(res);

	}
}

        那么你就可以拿下这道题喽!!!!

总结

        这道题如果是蓝桥杯的题目的话,恭喜你暴力至少可以过两个测试用例,但是牛客周赛是ACM赛制,所以你还是需要降低代码的时间复杂度,我们这里使用了一个数学的思维来解决这道题。所有的通过数学思路解决的题目都有一个通病就是,只要你想到了这道题的数学原理那么这道题就会变得无比简单,因为代码很好实现,就是数学的逻辑难想到罢辽~~~~~

        

        大家如果喜欢,点个赞呗,谢谢啦Thanks♪(・ω・)ノ

  • 16
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WenJGo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值