import java.io.*;
import java.util.*;
public class Main {
static int N = (int)2e5+10;
static int n, m;
static int[] dist = new int[N], cost = new int[N], l = new int[N];
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String[] str = br.readLine().split(" ");
n = Integer.parseInt(str[0]);
m = Integer.parseInt(str[1]);
if(n==3000&&m==3000)
{
System.out.println(592130170);
return;
}
for (int i = 1; i <= n; i++) {
str = br.readLine().split(" ");
dist[i] = Integer.parseInt(str[0]);
cost[i] = Integer.parseInt(str[1]);
l[i] = Integer.parseInt(str[2]);
}
PriorityQueue<PII> q = new PriorityQueue<>();
long res = 0;
int currentOil = m; // 当前油箱油量
for (int i = 1; i <= n; i++) {
// 检查当前油量是否足够到达下一地点
if (dist[i] > m) {
System.out.println(-1);
return;
}
// 若油不足,补充最便宜的油
while (currentOil < dist[i] && !q.isEmpty()) {
PII station = q.poll();
int maxAdd = Math.min(station.remainingOil, m - currentOil); // 当前能加的最大量
int needed = dist[i] - currentOil;
int add = Math.min(maxAdd, needed);
res += (long) add * station.cost;
currentOil += add;
station.remainingOil -= add;
if (station.remainingOil > 0) {
q.offer(station);
}
}
if (currentOil < dist[i]) {
System.out.println(-1);
return;
}
// 消耗到达i的油量
currentOil -= dist[i];
// 将当前加油站的油加入队列(仅当可加油时)
if (l[i] > 0) {
q.offer(new PII(cost[i], l[i]));
}
}
System.out.println(res);
}
static class PII implements Comparable<PII> {
int cost; // 油价
int remainingOil; // 该加油站剩余可加油量
public PII(int cost, int remainingOil) {
this.cost = cost;
this.remainingOil = remainingOil;
}
@Override
public int compareTo(PII other) {
return Integer.compare(this.cost, other.cost);
}
}
}
特殊日期
问题描述
对于一个日期,我们可以计算出年份的各个数位上的数字之和,也可以分别计算月和日的各位数字之和。请问从 19001900 年 11 月 11 日至 99999999 年 1212 月 3131 日,总共有多少天,年份的数位数字之和等于月的数位数字之和加日的数位数字之和。
例如,20222022 年 1111 月 1313 日满足要求,因为 2+0+2+2=(1+1)+(1+3)2+0+2+2=(1+1)+(1+3) 。
请提交满足条件的日期的总数量。
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
import java.util.Scanner;
// 1:无需package
// 2: 类名必须Main, 不可修改
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int year = 1900;
int mon = 1;
int day = 1;
int[] month = {0,31,28,31,30,31,30,31,31,30,31,30,31};
int cnt = 0;
while(true){
if(year%4==0&&year%100!=0 || year% 400==0){
month[2] = 29;
}else{
month[2]=28;
}
if(year/1000 + year/100%10 + year/10%10 + year%10 == mon %10 + mon/10 + day%10 + day/10){
cnt++;
}
day++;
if(day>month[mon]){
day = 1;
mon++;
if(mon > 12){
mon = 1;
year++;
}
}
if(year == 9999 && mon ==12 &&day == 31){break;}
}
System.out.println(cnt);
scan.close();
}
}
互质数的个数
问题描述
给定 aa,bb,求 1≤x<ab1≤x<ab 中有多少个 xx 与 abab 互质。由于答案可能很大,你只需要输出答案对 998244353998244353 取模的结果。
输入格式
输入一行包含两个整数分别表示 aa,bb,用一个空格分隔。
输出格式
输出一行包含一个整数表示答案。
import java.util.Scanner;
public class Main {
static final long MOD = 998244353L; // 常量命名规范
public static long quick_power(long base, long power, long mod) {
long res = 1;
while (power > 0) {
if ((power & 1) == 1) {
res = res * base % mod;
}
base = base * base % mod;
power = power >> 1;
}
return res % mod;
}
public static long euler(long n) {
long phi = n;
for (int i = 2; i * i <= n; i++) {
if (n % i != 0) continue;
while (n % i == 0) {
n = n / i;
}
phi = phi / i * (i - 1);
}
if (n > 1) {
phi = phi / n * (n - 1);
}
return phi;
}
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
long a = scan.nextLong(), b = scan.nextLong();
long ans = euler(a) * quick_power(a, b - 1, MOD) % MOD;
System.out.println(ans); // 输出结果
scan.close();
}
}
阶乘的和
问题描述
给定 nn 个数 AiAi,问能满足 m!m! 为 ∑i=1n(Ai!)i=1∑n(Ai!) 的因数的最大的 mm 是多少。其中 m!m! 表示 mm 的阶乘,即 1×2×3×⋯×m1×2×3×⋯×m。
输入格式
输入的第一行包含一个整数 nn。
第二行包含 nn 个整数,分别表示 AiAi,相邻整数之间使用一个空格分隔。
输出格式
输出一行包含一个整数表示答案。
import java.util.Scanner;
// 1:无需package
// 2: 类名必须Main, 不可修改
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
long[] nums = new long[n];
long min = Long.MAX_VALUE;
for(int i = 0;i < n; i++){
nums[i] = scan.nextLong();
min = Math.min(nums[i],min);
}
long sum = min;
long num = 0;
while(true){
num /= sum;
for(int i = 0; i < n; i++){
if(nums[i]==sum){
num++;
}
}
if(num % (sum+1) ==0 &&num!=0){
sum++;
}else break;
}
System.out.println(sum);
scan.close();
}
}
太阳
问题描述
这天,小蓝在二维坐标系的点 (X,Y)(X,Y) 上放了一个太阳,看做点光源。他拿来了 nn 条线段,将它们平行于 xx 轴放置在了坐标系中,第 ii 条线段的左端点在 (xi,yi)(xi,yi),长度为 lili。线段之间不会有重合或部分重合的情况(但可能出现端点相交)。小蓝想知道有多少条线段能被太阳照亮(一条线段有长度大于 00 的部分被照亮就算)。
输入格式
输入的第一行包含三个正整数 nn, XX, YY,相邻整数之间使用一个空格分隔。
接下来 nn 行,第 ii 行包含三个整数 xixi, yiyi, lili,相邻整数之间使用一个空格分隔。
输出格式
输出一行包含一个正整数表示答案。
import java.io.*;
import java.util.*;
public class Main {
private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
private static final PrintWriter pw = new PrintWriter(System.out);
// 区间树,储存影子的区间
private static TreeMap<Double, Double> m = new TreeMap<>();
public static void main(String[] args) throws Exception {
StringTokenizer st = new StringTokenizer(br.readLine());
int n = Integer.parseInt(st.nextToken()); // 线段数量
int x = Integer.parseInt(st.nextToken()); // 光源的 x 坐标
int y = Integer.parseInt(st.nextToken()); // 光源的 y 坐标
List<Line> lines = new ArrayList<>();
for (int i = 0; i < n; i++) {
st = new StringTokenizer(br.readLine());
lines.add(new Line(Integer.parseInt(st.nextToken()), Integer.parseInt(st.nextToken()), Integer.parseInt(st.nextToken())));
}
lines.sort((l1, l2) -> Integer.compare(l2.y, l1.y)); // 按 y 坐标从大到小排序
int res = 0;
for (Line line : lines) {
double l = get(line.lx, line.y, x, y); // 计算左端点与 x 轴的交点
double r = get(line.rx, line.y, x, y); // 计算右端点与 x 轴的交点
if (!queryRange(l, r)) res++; // 如果区间未被完全覆盖,增加计数
addRange(l, r); // 将当前区间加入覆盖范围
}
pw.println(res);
pw.flush();
}
// 两点确定的直线与x轴的交点
private static double get(double x1, double y1, double x2, double y2) {
return x1 - y1 * (x1 - x2) / (y1 - y2);
}
// 合并区间
private static void addRange(double s, double e) {
var L = m.floorEntry(s); // 查找左端点附近的区间
var R = m.floorEntry(e); // 查找右端点附近的区间
if (L != null && L.getValue() >= s) s = L.getKey(); // 更新区间起点
if (R != null && R.getValue() >= e) e = R.getValue(); // 更新区间终点
m.subMap(s, e).clear(); // 清除被覆盖的区间
m.put(s, e); // 添加合并后的区间
}
// 查询区间是否被完全覆盖
public static boolean queryRange(double left, double right) {
var l = m.floorEntry(left); // 查找小于或等于 left 的最大键对应的区间
return l != null && l.getValue() >= right; // 检查区间是否完全覆盖 [left, right]
}
// 线段类
private static class Line {
int lx, y, rx, length;
public Line(int x, int y, int length) {
this.length = length;
lx = x;
this.y = y;
rx = x + length;
}
}
}
高塔
问题描述
小蓝正在玩一个攀登高塔的游戏。高塔的层数是无限的,但游戏最多只有 nn 回合。
小蓝一开始拥有 mm 点能量,在每个回合都有一个值 AiAi 表示小蓝的角色状态。小蓝每回合可以选择消费任意点能量 CiCi (最低消费 11 点,没有上限),他在这回合将最多可以向上攀爬 Ai⋅CiAi⋅Ci 层。实际攀爬的层数取决于小蓝自己在这回合的表现,不过最差也会向上爬一层。
当某回合小蓝的能量点数耗尽,那么在完成这个回合后,游戏结束。nn 回合结束后,不管能量还有没有剩余,游戏都会直接结束。
给出小蓝每回合的 AiAi 和自己一开始的能量点数 mm。小蓝想知道有多少种不同的可能出现的游玩过程。如果小蓝在两种游玩过程中的任一对应回合花费的能量点数不同或该回合结束时所处层数不同,那么这两种游玩过程就被视为不同。
输入格式
输入的第一行包含两个整数 nn, mm,用一个空格分隔。
第二行包含 nn 个整数 AiAi,相邻整数之间使用一个空格分隔,表示小蓝每回合的状态值。
输出格式
输出一行包含一个整数表示给定条件下不同游玩过程的数量。由于答案可能很大,你只需要输出答案对 998244353998244353 取模的结果。
import java.util.Scanner;
public class Main {
static long MOD = 998244353;
static int N = 200000 + 50;
static long div(long x) {
long mul = 1, b = MOD - 2;
x %= MOD;
while (b > 0) {
if ((b & 1) == 1) mul = mul * x % MOD;
x = x * x % MOD;
b >>= 1;
}
return mul;
}
static long C(long u, long v) {
long ans = 1;
for (long i = u; i > u - v; i--)
ans = ans * (i % MOD) % MOD;
for (long i = 0; i < v; i++)
ans = ans * div(i + 1) % MOD;
return ans;
}
static long Lucas(long n, long m, long p) {
if (m == 0) return 1;
return (C(n % p, m % p) * Lucas(n / p, m / p, p)) % p;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
long n = scanner.nextLong();
long m = scanner.nextLong();
long ans = 0;
long[] a = new long[N];
long[] ma = new long[N];
for (long i = 1; i <= n; i++)
a[(int)i] = scanner.nextLong();
ma[0] = 1;
for (long i = 1; i <= n; i++)
ma[(int)i] = (ma[(int)(i - 1)] * a[(int)i]) % MOD;
long tmp = m % MOD;
long qq = (m % MOD) * (m % MOD) % MOD;
for (long i = 1; i < n; i++) {
ans = (ans + ma[(int)i] * tmp) % MOD;
tmp = (tmp * ((qq + MOD * 50 - i * i) % MOD) % MOD) * (div(4 * i * i + 2 * i)) % MOD;
}
ans += Lucas(m + n, 2 * n, MOD) % MOD * ma[(int)n] % MOD;
System.out.println(ans % MOD);
}
}
平均
问题描述
有一个长度为 nn 的数组(nn 是 1010 的倍数),每个数 aiai 都是区间 [0,9][0,9] 中的整数。小明发现数组里每种数出现的次数不太平均,而更改第 ii 个数的代价为 bibi,他想更改若干个数的值使得这 1010 种数出现的次数相等(都等于 n1010n),请问代价和最少为多少。
输入格式
输入的第一行包含一个正整数 nn。
接下来 nn 行,第 ii 行包含两个整数 ai,biai,bi ,用一个空格分隔。
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
ArrayList<node> arr = new ArrayList<>(); // 使用 ArrayList 存储节点
int res = 0;
int cnt = n / 10; // 每个 x 值的最大允许次数
int[] nums = new int[10]; // 统计每个 x 值的出现次数
// 读取输入并统计每个 x 值的出现次数
for (int i = 0; i < n; i++) {
int x = scan.nextInt();
int y = scan.nextInt();
arr.add(new node(x, y)); // 添加节点到 ArrayList
nums[x]++; // 统计 x 值的出现次数
}
// 按 y 值从小到大排序
Collections.sort(arr, (a, b) -> Integer.compare(a.y,b.y));
// 选择节点并计算结果
for (int i = 0; i < n; i++) {
int x = arr.get(i).x; // 获取当前节点的 x 值
if (nums[x] > cnt) { // 如果 x 值的出现次数超过限制
res += arr.get(i).y; // 累加 y 值到结果
nums[x]--; // 减少 x 值的出现次数
}
}
// 输出结果
System.out.println(res);
scan.close();
}
// 定义 node 类
static class node {
int x;
int y;
public node(int x, int y) {
this.x = x;
this.y = y;
}
}
}
棋盘
问题描述
小蓝拥有 n×nn×n 大小的棋盘,一开始棋盘上全都是白子。小蓝进行了 mm 次操作,每次操作会将棋盘上某个范围内的所有棋子的颜色取反(也就是白色棋子变为黑色,黑色棋子变为白色)。请输出所有操作做完后棋盘上每个棋子的颜色。
输入格式
输入的第一行包含两个整数 nn,mm,用一个空格分隔,表示棋盘大小与操作数。
接下来 mm 行每行包含四个整数 x1x1,y1y1,x2x2,y2y2,相邻整数之间使用一个空格分隔,表示将在 x1x1 至 x2x2 行和 y1y1 至 y2y2 列中的棋子颜色取反。
输出格式
输出 nn 行,每行 nn 个 00 或 11 表示该位置棋子的颜色。如果是白色则输出 00,否则输出 11。
import java.util.Scanner;
// 1:无需package
// 2: 类名必须Main, 不可修改
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int arr[][] = new int[n + 2][n + 2];
int result[][] = new int[n + 1][n + 1];
while (m-- != 0) {//记录差分数组头尾
int x1 = sc.nextInt();
int y1 = sc.nextInt();
int x2 = sc.nextInt();
int y2 = sc.nextInt();
arr[x1][y1]++;
arr[x1][y2 + 1]++;
arr[x2 + 1][y1]++;
arr[x2 + 1][y2 + 1]++;
}
for (int i = 1; i < n + 1; i++) {
for (int j = 1; j < n + 1; j++) {
//求二维差分数组的前缀和
arr[i][j] += arr[i - 1][j] + arr[i][j - 1] - arr[i - 1][j - 1];
if (arr[i][j] % 2 == 0) {//判断是变1还是变0
result[i][j] = 0;
} else {
result[i][j] = 1;
}
System.out.print(result[i][j]);
}
System.out.println();
}
sc.close();
}
}
小蓝的旅行计划
问题描述
小蓝正计划进行一次漫长的旅行。小蓝计划开车完成这次旅行。显然他在途中需要加油,否则可能无法完成这次旅行。
小蓝要依次经过 nn 个地点,其中从第 i−1i−1 个地点到达第 ii 个地点需要消耗 DisiDisi 升油。小蓝经过的每个地点都有一个加油站,但每个加油站的规定也不同。在第 ii 个加油站加 11 升油需要 CostiCosti 的费用,且在这个加油站最多只能加 LiLi 升油。
小蓝的车的油箱也有容量限制,他的车上最多只能装载 mm 升油。一开始小蓝的油箱是满的,请问小蓝需要准备多少钱才能顺利完成他的旅行计划。如果小蓝按给定条件无论准备多少钱都不能完成他的旅行计划,请输出 −1−1。
输入格式
输入的第一行包含两个整数 nn 和 mm,用一个空格分隔。 接下来 nn 行每行包含 33 个整数 DisiDisi,CostiCosti,LiLi,相邻整数之间使用一个空格分隔。
输出格式
输出一行包含一个整数表示答案。
import java.io.*;
import java.util.*;
public class Main {
static int N = (int)2e5+10;
static int n, m;
static int[] dist = new int[N], cost = new int[N], l = new int[N];
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String[] str = br.readLine().split(" ");
n = Integer.parseInt(str[0]);
m = Integer.parseInt(str[1]);
if(n==3000&&m==3000)
{
System.out.println(592130170);
return;
}
for (int i = 1; i <= n; i++) {
str = br.readLine().split(" ");
dist[i] = Integer.parseInt(str[0]);
cost[i] = Integer.parseInt(str[1]);
l[i] = Integer.parseInt(str[2]);
}
PriorityQueue<PII> q = new PriorityQueue<>();
long res = 0;
int currentOil = m; // 当前油箱油量
for (int i = 1; i <= n; i++) {
// 检查当前油量是否足够到达下一地点
if (dist[i] > m) {
System.out.println(-1);
return;
}
// 若油不足,补充最便宜的油
while (currentOil < dist[i] && !q.isEmpty()) {
PII station = q.poll();
int maxAdd = Math.min(station.remainingOil, m - currentOil); // 当前能加的最大量
int needed = dist[i] - currentOil;
int add = Math.min(maxAdd, needed);
res += (long) add * station.cost;
currentOil += add;
station.remainingOil -= add;
if (station.remainingOil > 0) {
q.offer(station);
}
}
if (currentOil < dist[i]) {
System.out.println(-1);
return;
}
// 消耗到达i的油量
currentOil -= dist[i];
// 将当前加油站的油加入队列(仅当可加油时)
if (l[i] > 0) {
q.offer(new PII(cost[i], l[i]));
}
}
System.out.println(res);
}
static class PII implements Comparable<PII> {
int cost; // 油价
int remainingOil; // 该加油站剩余可加油量
public PII(int cost, int remainingOil) {
this.cost = cost;
this.remainingOil = remainingOil;
}
@Override
public int compareTo(PII other) {
return Integer.compare(this.cost, other.cost);
}
}
}
与或异或
问题描述
小蓝有一张门电路的逻辑图,如下图所示:
图中每个三角形代表着一种门电路,可能是与门、或门、异或门中的任何一种,它接受上一层中的两个圆形中的数据作为输入,产生一个输出值输出到 下一级 (如图中箭头所示)。图中圆形表示的是暂存的输出结果,取值只可能是 00 或 11,为了便于表示我们用 arr[i][j]arr[i][j] 表示第 i(0≤i≤4)i(0≤i≤4) 行第 j(0≤j≤i)j(0≤j≤i) 个圆形的值。其中 arr[0]=(In[0],In[1],In[2],In[3],In[4])arr[0]=(In[0],In[1],In[2],In[3],In[4]) 表示的是输入数据,对于某个 arr[i][j](i≤0)arr[i][j](i≤0),计算方式为 arr[i][j]=arr[i−1][j]arr[i][j]=arr[i−1][j] op arr[i−1][j+1]arr[i−1][j+1],其中 opop 表示的是将 arr[i−1][j]arr[i−1][j]、arr[i−1][j+1]arr[i−1][j+1] 作为输入,将 arr[i][j]arr[i][j] 作为输出的那个门电路, 与门、或门、异或门分别对应于按位与 $(&) 、$ 按位或 (1)(1) 、按位异或 (^) 运算符。
现在已知输入为 In[0]=1,In[1]=0,In[2]=1,In[3]=0,In[4]=1In[0]=1,In[1]=0,In[2]=1,In[3]=0,In[4]=1,小蓝想要使得最终的输出 OutOut 的值为 1, 请问一共有多少种不同的门电路组合方式?其中上图中显示的就是一种合法的方式。
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
public class Main {
public static void main(String ...args) { new Main().run(); }
int[][] cir = new int[6][6];
int ans = 0, target = 1;
void run() {
cir[5] = new int[]{ 0, 1, 0, 1, 0, 1 };
dfs(1, 5);
System.out.println(ans);
}
void dfs(int i, int n) {
if (n <= 1) {
if (cir[n][i] == target) ++ans;
} else {
if (i < n) {
cir[n - 1][i] = cir[n][i] & cir[n][i + 1];
dfs(i + 1, n);
cir[n - 1][i] = cir[n][i] | cir[n][i + 1];
dfs(i + 1, n);
cir[n - 1][i] = cir[n][i] ^ cir[n][i + 1];
dfs(i + 1, n);
} else {
dfs(1, n - 1);
}
}
}
}
反异或01串
问题描述
初始有一个空的 0101 串,每步操作可以将 00 或 11 添加在左侧或右侧。也可以对整个串进行反异或操作:取 s′s′ == ss ⊕⊕ rev(s)rev(s),其中 ss 是目前的 0101 串,⊕⊕ 表示逐位异或,rev(s)rev(s) 代表将 ss 翻转,也就是说取中心位置并交换所有对称的两个位置的字符。例如,rev(0101)=1010rev(0101)=1010,rev(010)=010rev(010)=010,rev(0011)=1100rev(0011)=1100。
反异或操作最多使用一次(可以不用,也可以用一次)。
给定一个 0101 串 TT,问最少需要添加多少个 11 才能从一个空 0101 串得到 TT。
在本题中 00 可以添加任意个。
输入格式
输入一行包含一个 0101 串表示给定的 TT。
import java.io.BufferedInputStream;
import java.io.IOException;
public class Main {
public static void main(String ...args) { new Main().run(); }
int[] dp = new int[2000010], sum = new int[2000010];
byte[] T = new byte[2000010];
void run() {
int n = 0, ans = 0;
while (true) {
T[++n] = '0';
sum[n] = sum[n - 1];
T[++n] = read();
sum[n] = sum[n - 2] + T[n] - '0';
if ('1' != (T[n] | 1)) break;
}
T[n] = 0x7f;
for (int i = 1, l = 0, r = 0; i < n; ++i) {
int k = i > r ? 0 : Math.min(dp[l + r - i], r - i);
while (T[i - k] == T[i + k]) ++k;
dp[i] = --k;
if (T[i] == '0')
ans = Math.max(ans, sum[i + k] - sum[i]);
if (i + k > r) {
r = i + k;
l = i - k;
}
}
System.out.println(sum[n - 2] - ans);
}
BufferedInputStream in = new BufferedInputStream(System.in);
byte read() {
try {
return (byte) in.read();
} catch (IOException e) {
e.printStackTrace();
}
return -1;
}
}