牛客巅峰赛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 1≤n≤1001
对于100%的数据: 1 ≤ n ≤ 1 e 9 1≤n≤1e9 1≤n≤1e9
对于每个n,返回一个严格四舍五入保留两位小数的字符串。
比如概率为0.372的话,返回字符串"0.37"。
概率为0.957的话,返回字符串"0.96"。
(注意,返回的字符串不带引号)
题解
题意就是让我们算 0. 5 n − 1 0.5^{n-1} 0.5n−1并四舍五入保留两位小数,可以选择循环累乘。我选择了直接调用函数
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(2≤n≤105)个玩偶,牛牛打算把这n个玩偶摆在桌子上,桌子的形状的长条形的,可以看做一维数轴。 桌子上有 M 个互不相交的区间 ( 1 ≤ M ≤ 1 0 5 ) (1 ≤M≤10^5) (1≤M≤105),这些区间上面可以放玩偶。一个位置只能放一个玩偶,玩偶之间的距离越大越美观,牛牛想最大化 D 的值,其中 D 为最近的两个玩偶之间的距离。请帮牛牛求出 D 的最大可能值。
样例1
输入
5,3,[[0,2],[4,7],[9,9]]
返回
2
备注
区间长度 ≤ 2 31 − 1 \leq 2^{31} -1 ≤231−1
题解
个人认为这道题对于刷题量少的人来说是比较难的(包括我)。题面意思就是让我们求所有最小的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(l≤r),牛牛需要算出
∑
r
i
=
l
∑
j
=
i
+
1
r
a
i
∗
a
j
∑^{i=l}_r∑^r_{j=i+1}a_i∗a_j
∑ri=l∑j=i+1rai∗aj的值。
但是牛牛算完之后你不能不确定他算得对不对,为了验证他的答案,你决定自己算一遍。
为了不输出过大的答案,假设答案为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; 参数a为vector<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; 参数query为vector<int>,依次为l1,r1,l2,r2,...,lq,rq;
n 为 数 组 a 长 度 , q 为 询 问 次 数 。 n为数组a长度,q为询问次数。 n为数组a长度,q为询问次数。
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<=100000,1<=ai<=100000,1<=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=a1∗a2+a1∗a3+a1∗a4+a2∗a3+a2∗a4+a3∗a4
而我们来看九九乘法表
1 ∗ 1 1*1 1∗1
2 ∗ 1 2*1 2∗1 , 2 ∗ 2 2 *2 2∗2
3 ∗ 1 3*1 3∗1, 3 ∗ 2 3*2 3∗2, 3 ∗ 3 3*3 3∗3
4 ∗ 1 4*1 4∗1, 4 ∗ 2 4*2 4∗2, 4 ∗ 3 4*3 4∗3, 4 ∗ 4 4*4 4∗4
注意观察, a n s ans ans各项的下标是不是刚好对应着九九乘法表的下标,也就是说 a n s ans ans其实就等于九九乘法表斜对角线下方的所有元素之和
我们可以选择采用二维前缀和,但也可以化为一维前缀和。
我们如果补全九九乘法表的话
1 ∗ 1 1*1 1∗1, 1 ∗ 2 1*2 1∗2, 1 ∗ 3 1*3 1∗3, 1 ∗ 4 1*4 1∗4
2 ∗ 1 2*1 2∗1 , 2 ∗ 2 2 *2 2∗2, 2 ∗ 3 2*3 2∗3, 2 ∗ 4 2*4 2∗4
3 ∗ 1 3*1 3∗1, 3 ∗ 2 3*2 3∗2, 3 ∗ 3 3*3 3∗3, 3 ∗ 4 3*4 3∗4
4 ∗ 1 4*1 4∗1, 4 ∗ 2 4*2 4∗2, 4 ∗ 3 4*3 4∗3, 4 ∗ 4 4*4 4∗4
有没有惊奇的发现这个矩阵的元素和就是 ( 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)−1∗1−2∗2−3∗3−4∗4)/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;
}
}