牛客巅峰赛S2第四场题解(青铜白银组)

牛客巅峰赛S2第四场题解(青铜白银组)


欢迎关注我的公众号ACJavaBear,一起学Java


1.牛牛掷硬币

题目描述

牛牛最近很喜欢掷硬币,由于他今天很无聊,所以他在家掷了n次硬币,如果这n次硬币全部朝上或者全部朝下牛牛就很开心,请问牛牛开心的概率是多少。(每次掷硬币朝上的概率与朝下的概率相同)

样例1

输入

1

返回

"1.00"

说明

概率为1,四舍五入保留两位小数的字符串为"1.00"
样例2

输入

5

返回

"0.06"

说明

概率为0.0625,四舍五入保留两位小数的字符串为"0.06"
备注

对于50%的数据: 1 ≤ n ≤ 1001 1≤n≤1001 1n1001

对于100%的数据: 1 ≤ n ≤ 1 e 9 1≤n≤1e9 1n1e9

对于每个n,返回一个严格四舍五入保留两位小数的字符串。

比如概率为0.372的话,返回字符串"0.37"。

概率为0.957的话,返回字符串"0.96"。

(注意,返回的字符串不带引号)

题解

题意就是让我们算 0. 5 n − 1 0.5^{n-1} 0.5n1并四舍五入保留两位小数,可以选择循环累乘。我选择了直接调用函数

public class Solution {
    public String Probability (int n) {
        return String.format("%.2f",Math.pow(0.5,n-1));
    }
}

2.牛牛摆玩偶

题目描述

牛牛有 n ( 2 ≤ n ≤ 1 0 5 ) n(2≤n≤10^5) n(2n105)个玩偶,牛牛打算把这n个玩偶摆在桌子上,桌子的形状的长条形的,可以看做一维数轴。 桌子上有 M 个互不相交的区间 ( 1 ≤ M ≤ 1 0 5 ) (1 ≤M≤10^5) 1M105,这些区间上面可以放玩偶。一个位置只能放一个玩偶,玩偶之间的距离越大越美观,牛牛想最大化 D 的值,其中 D 为最近的两个玩偶之间的距离。请帮牛牛求出 D 的最大可能值。

样例1

输入

5,3,[[0,2],[4,7],[9,9]]

返回

2
备注

区间长度 ≤ 2 31 − 1 \leq 2^{31} -1 2311

题解

个人认为这道题对于刷题量少的人来说是比较难的(包括我)。题面意思就是让我们求所有最小的D中最大的一个。

对于这类问题求最小的XX中的最大值 或 求最大的XX中的最小值都可以往二分想想。而想要用二分就需要先排序,故第一步我们先给区间排序。

第二步,如何摆放玩偶?一个区间一个区间摆,并贪心考虑,尽量按照某个间距D,摆在左边。所以第一个玩偶我们肯定放在第一个区间的起始位置。具体思路见代码。

import java.util.*;
/*
 * public class Interval {
 *   int start;
 *   int end;
 *   public Interval(int start, int end) {
 *     this.start = start;
 *     this.end = end;
 *   }
 * }
 */
public class Solution {
    public int doll (int n, int m, Interval[] intervals) {
        //lambda表达式,按左端点给区间排序
        Arrays.sort(intervals,(a,b) -> a.start - b.start);
        //在整个区间里面找间距
        long l = 0, r = (1l << 31) - 1;
        while(l < r){
            long mid = l + (r - l) / 2 + 1;
            if(check(intervals,mid,n)){
                //按mid摆可以摆大于等于n个玩偶,说明间距小了,所以在后半部分搜索
                l = mid;
            }else r = mid - 1; //间距大了,在前半部分搜索
        }
        return (int) l;
        
    }
    
    public boolean check(Interval[] intv,long mid,int n){
        int num = 1; //num为摆放的玩偶数,第一个玩偶摆在起始位置
        long now = intv[0].start + mid;//现在判断离起点间距为mid的位置能不能摆放玩偶
        for(int i = 0; i < intv.length; i++){//遍历区间
            //如果当前位置已经在区间外了,说明这个区间摆不了,判断下一个区间
            if(now > intv[i].end) continue;  
            //有可能当前位置落在两区间的间隙里面,所以为防止这种情况,在now与区间起点取一个最大值
            now = Math.max(now,(long) intv[i].start);
            //计算这个区间,以mid为间隔摆放,一共可以摆放的玩偶数
            long nums = (intv[i].end - now) / mid + 1;
            //统计玩偶数
            num += nums;
            //更新当前位置
            now = now + mid * nums;
        }
        //true表示按mid间距摆放可以放大于等于n个玩偶
        //false反之
        return num >= n;
    }
}

3.交叉乘

题目描述

上小学二年级的牛牛已经精通整数的加法和乘法。现在你要考考他。你先给出一个整数数组 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an,然后每次你会给一对整数 l , r ( l ≤ r ) l,r(l≤r) l,r(lr),牛牛需要算出 ∑ r i = l ∑ j = i + 1 r a i ∗ a j ∑^{i=l}_r∑^r_{j=i+1}a_i∗a_j ri=lj=i+1raiaj的值。
但是牛牛算完之后你不能不确定他算得对不对,为了验证他的答案,你决定自己算一遍。

为了不输出过大的答案,假设答案为ans,请输出ans % 1,000,000,007。

提示: 在%1,000,000,007的意义下/2相当于*500,000,004

样例1

输入

[1,2,5,3,4],[1,4,2,5,2,2]

返回

[41,71,0]

说明

第一个询问,l=1,r=4,ans=1*2+1*5+1*3+2*5+2*3+5*3=41;
第二个询问,l=2,r=5,ans=2*5+2*3+2*4+5*3+5*4+3*4=71;
第三个询问,l=2,r=2,ans=0。
备注

参 数 a 为 v e c t o r < i n t > , 依 次 为 a 1 , a 2 , . . . , a n ; 参数a为vector<int>,依次为a_1,a_2,...,a_n; avector<int>a1,a2,...,an

参 数 q u e r y 为 v e c t o r < i n t > , 依 次 为 l 1 , r 1 , l 2 , r 2 , . . . , l q , r q ; 参数query为vector<int>,依次为l_1,r_1,l_2,r_2,...,l_q,r_q; queryvector<int>l1,r1,l2,r2,...,lq,rq

n 为 数 组 a 长 度 , q 为 询 问 次 数 。 n为数组a长度,q为询问次数。 naq

30%数据满足 1 < = n , q < = 1000 1<=n,q<=1000 1<=n,q<=1000

100% 数据满足 1 < = n , q < = 100000 , 1 < = a i < = 100000 , 1 < = l i < = r i < = n 1<=n,q<=100000,1<=a_i<=100000,1<=l_i<=r_i<=n 1<=n,q<=1000001<=ai<=1000001<=li<=ri<=n

题解

主要思路:九九乘法表+前缀和

我们可以来观察一下,以样例中 l = 1 , r = 4 l = 1,r = 4 l=1,r=4 为例

a n s = a 1 ∗ a 2 + a 1 ∗ a 3 + a 1 ∗ a 4 + a 2 ∗ a 3 + a 2 ∗ a 4 + a 3 ∗ a 4 ans = a_1 * a_2 + a_1 * a_3 + a_1 * a_4 +a_2*a_3+a_2*a_4+a_3*a_4 ans=a1a2+a1a3+a1a4+a2a3+a2a4+a3a4

而我们来看九九乘法表

1 ∗ 1 1*1 11

2 ∗ 1 2*1 21 2 ∗ 2 2 *2 22

3 ∗ 1 3*1 31 3 ∗ 2 3*2 32 3 ∗ 3 3*3 33

4 ∗ 1 4*1 41 4 ∗ 2 4*2 42 4 ∗ 3 4*3 43 4 ∗ 4 4*4 44

注意观察, a n s ans ans各项的下标是不是刚好对应着九九乘法表的下标,也就是说 a n s ans ans其实就等于九九乘法表斜对角线下方的所有元素之和

我们可以选择采用二维前缀和,但也可以化为一维前缀和。

我们如果补全九九乘法表的话

1 ∗ 1 1*1 11 1 ∗ 2 1*2 12 1 ∗ 3 1*3 13 1 ∗ 4 1*4 14

2 ∗ 1 2*1 21 2 ∗ 2 2 *2 22 2 ∗ 3 2*3 23 2 ∗ 4 2*4 24

3 ∗ 1 3*1 31 3 ∗ 2 3*2 32 3 ∗ 3 3*3 33 3 ∗ 4 3*4 34

4 ∗ 1 4*1 41 4 ∗ 2 4*2 42 4 ∗ 3 4*3 43 4 ∗ 4 4*4 44

有没有惊奇的发现这个矩阵的元素和就是 ( 1 + 2 + 3 + 4 ) ∗ ( 1 + 2 + 3 + 4 ) (1+2+3+4)*(1+2+3+4) (1+2+3+4)(1+2+3+4),且矩阵中的元素关于斜对角线对称

那么 a n s ans ans等于什么呢?其实已经很明白了,即 ( ( 1 + 2 + 3 + 4 ) ∗ ( 1 + 2 + 3 + 4 ) − 1 ∗ 1 − 2 ∗ 2 − 3 ∗ 3 − 4 ∗ 4 ) / 2 ((1+2+3+4)*(1+2+3+4)-1*1-2*2-3*3-4*4)/2 ((1+2+3+4)(1+2+3+4)11223344)/2

所以我们可以处理出两个前缀和数组,一个是元素的前缀和,一个是元素的平方的前缀和。

通项公式相必大家能自己推导了。我们看代码。

public class Solution {
    public int[] getSum (int[] a, int[] query) {
       int mod = 1000000007;
        int n = a.length;
        //存储元素前缀和
        long[] sum = new long[n+1];
        //存储元素的平方的前缀和
        long[] powSum = new long[n+1];
        //初始化签注和数组,一定要注意,不要越界
        for(int i = 0; i < n; i++){
            sum[i+1] = (sum[i] + a[i]) % mod;
            powSum[i+1] = powSum[i] + (long) a[i]* a[i]  % mod;
        }
        //存储答案
        int[] res = new int[query.length/2];
        for(int i = 0; i < query.length; i+=2){
            int l = query[i];
            int r = query[i+1];
            //通项公式,加mod防止相减出现负数
            long t = ((sum[r]  - sum[l-1] + mod) )*((sum[r]  - sum[l-1] + mod));
            t -= powSum[r] - powSum[l-1];
            t = (t % mod + mod) % mod;
            //根据提示,把除以2换成下面的方式
            t *= 500000004;
            t %= mod;
            res[i/2] = (int) t;
        }
        return res;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值