A题
描述
在一个公园修建最少的路灯,照亮整片公园,路灯的照亮范围是所在边界的附近两块。
输入:第一行为本次测试用例数量(1<=t<=104),之后为公园的长宽(1<=n,m<=104)
输出需要的路灯数
思路
就跟以前看过一个盖方块的思维题一样给一个棋盘(国际象棋那种),然后用一个1*2的方块来逐一覆盖棋盘,方块之间不能有重叠或者漏盖,请问能否覆盖完全。
这个思维题很简单,如果这个面积可以被2整除,那必然可以完全覆盖,因为它的最小单位面积就是2。
再回到这道题,我们想要最少的路灯,自然也与上面同理,就是“方块”(灯光范围)之间不能有重叠,那么直接对面积除以2,如果不能被2整除,那么就单独用一盏灯照亮。
import java.util.Scanner;
/**
* @创建人 YDL
* @创建时间 2020/5/9 22:34
* @描述
*/
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
for(int i=0;i<n;i++){
int m = sc.nextInt();
int nn = m*sc.nextInt();
int ans = nn>>1;
ans = (nn&1)==0?ans:ans+1;
System.out.println(ans);
}
}
}
B题
描述
一个老太太M,想约人举行啥仪式,但是老太太们都有点牌面,必须当时能到场的人数大于或等于自己要求的人数才来(M和自己也算在其中),M可以自行选择约老太太的顺序。等价于约了就会闪现那种,老太太如果发现人数不足自己的要求就闪走,那么最后到场的能有多少个老太太。
输入:第一行t测试用例的数量(1≤t≤104),测试用例结构,第一行,老太太人数(1≤n≤105),第二行老太太的要求人数(1≤ai≤2⋅105).
输出:能到场的老太太人数k (1≤k≤n+1)
思路
这题就用贪心的思路,首先将老太太排序,优先邀请要求人数少的,不过需要注意的是,人数有时会低于要求,可是之后要求相同的老太太都邀请来了,那就没问题,所以得考虑到最后。
import java.util.Arrays;
import java.util.Scanner;
/**
* @创建人 YDL
* @创建时间 2020/5/9 22:34
* @描述
*/
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
for(int i=0;i<n;i++){
int n1 = sc.nextInt();
int[] nais = new int[n1];
for(int j=0;j<n1;j++){
nais[j] = sc.nextInt();
}
Arrays.parallelSort(nais);
int ans = 1,pre = 1;
for(int j=0;j<n1;j++){
if(nais[j]<ans+pre){
ans+=pre;
pre = 1;
}else{
pre++;
}
}
System.out.println(ans);
}
}
}
C题
描述
给定了一个矩阵的规则,大概就是斜着递增。
然后给出某两个坐标,求起始点到终点的可能路径(经过的点的和不相同)和。
输入:测试样例的数量t (1≤t≤57179) ,测试样例的格式起始坐标(x1,y2),终点坐标(x2,y2)(1≤x1≤x2≤109, 1≤y1≤y2≤109)
输出:路径之和
思路
做的时候没细看题目无脑想成计数dp了…这题非要用dp也可以再用一个Set存储到达目标位置时的和。不过就有点暴力了。看了下数据规模,肯定过不了的。
那么只能取巧,选择都是在每一层选一个点(斜着算一层),而每层对应每层选择至少会比上一次选择大2,所以我们并不需要老实的模拟从(x1,y1)->(x2,y2).有个规律就是所得结果必然会是一段连续数列,因为每次选择都会在上一层原基础上加2,这个所有路径都是,所以都可以忽略不记,本质上最每层斜着过来就是1,2,3,4,5…
1 | 1 | 1 | 1 | 1 | … |
---|---|---|---|---|---|
2 | 2 | 2 | 2 | 2 | … |
3 | 3 | 3 | 3 | 3 | … |
… |
本质上就是求这段连续的和为多少,可以算出为(x-1)+Σiy i —— Σiy i+y*(x-1),求得为(x-1)*(y-1)+1.
import java.util.Arrays;
import java.util.Scanner;
/**
* @创建人 YDL
* @创建时间 2020/5/9 22:34
* @描述
*/
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
for(int i=0;i<n;i++){
int x0 = sc.nextInt();
int y0 = sc.nextInt();
int x1 = sc.nextInt();
int y1 = sc.nextInt();
long ans = (long)(x1-x0)*(y1-y0)+1;
System.out.println(ans);
}
}
}
D题
描述
去一个城市旅行,但是那个城市的日历很奇怪,每个月的日期不一样,在每月的i日去,可以获得i的快乐指数,可以停留x日,这次旅行最多能够获取多少次快乐指数?(可以跨年)
输入:
第一行:一年有几月(n (1≤n≤2⋅105)),能够停留几日(x 少于一年的总天数)
第二行:每月天数(d1,d2,d3… (1≤di≤106) )
输出:快乐指数
思路
当为最大时,开始点或者结束点肯定是在一个月份的结束位置。
因为不同大小的月份数,它们前面的都是一致的,只有后面的天数才是最有价值的(比较大嘛),所以我们可以尝试,以每个月都作为开头,然后查找到可供它选择的所有天数。
因为可以跨年嘛,所以这是个环,先把环展开(把读入的月份数复制一份。)而且天数肯定是随着前面所需天数递增的,如果能够度过这个月份,那么和为((1+di)*di )/2,如果不行,那么只取剩余天数的等差数列和。
我们也不用每个月都算一次,直接将完整的月份等差数列和与之前月份的和存入一个数组中,那么如果要求某个区间月份的天数和就可以直接相减了。
因为这是个单递增的数列,所以我们可以使用二分查找的方式,查找选择某月作为结尾时,它能选择到的范围。
然后再用这个范围的总天数去减去x,这个差就是开始的天数之前的天数。
再用范围总天数减去之前的天数和即可。
import java.util.Scanner;
/**
* @创建人 YDL
* @创建时间 2020/5/9 22:34
* @描述
*/
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
long x = sc.nextLong();
long[] m = new long[(n<<1)+1];
long[] days = new long[(n<<1)+1];
long[] sums = new long[(n<<1)+1];
for(int i=1;i<=n;i++){
m[i] = sc.nextLong();
m[i+n] = m[i];
}
int n2 = (n<<1);
for(int i=1;i<=n2;i++){
days[i] = days[i-1]+m[i];
sums[i] = sums[i-1]+((m[i]+1)*m[i]>>1);
}
long max =0;
for(int i=n2;i>=n+1;i--){
int left=1;
int right=i;
int ans=i;
while(right>=left){
int mid=left+right>>1;
if(days[i]-days[mid]<x){
right=mid-1;
ans=mid;
}
else {
left = mid + 1;
}
}
long temp=days[i]-days[ans-1]-x;
max=Math.max(sums[i]-sums[ans-1]-(((temp+1)*temp>>1)),max);
}
System.out.println(max);
}
}