解析这个思路 import java.util.Scanner; import java.util...
作成日: 2025年12月27日
作成日: 2025年12月27日
解析这个思路 import java.util.Scanner;
import java.util.*;
import java.util.stream.Stream;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String input_str = in.nextLine();
int[] nums = split(input_str, " ");
int x = nums[0];
int y = nums[1];
int count_x = nums[2];
int count_y = nums[3];
textint left = 1; int right = Integer.MAX_VALUE; while (true) { if(left >= right){ break; } else { int mid = (left + right) >> 1; int target = Math.max(0, count_x - mid / y + mid / (x * y)) + Math.max(0, count_y - mid / x + mid / (x * y)); if (mid - mid / x - mid / y + mid / (x * y) >= target) { right = mid; } else { left = mid + 1; } } } System.out.println(left); } public static int[] split(String input_str,String chars){ String[] tmp2 = input_str.split(chars); int[] results = new int[tmp2.length]; for (int i = 0; i < tmp2.length; i++) { results[i] = Integer.parseInt(tmp2[i]); } return results; }
}
这段代码的核心思路是:二分答案 k,并且对某个候选的 mid=k,用**容斥 + “强制分配 / 可选分配”**来判断能不能在 [1..mid] 里完成派遣。
规则:
x<y(因此 lcm(x,y)=x*y,不会有更复杂的公因数问题)对 [1..mid] 里的员工编号,按整除关系分 4 类:
OnlyX:mid 中 “是 x 的倍数但不是 y 的倍数”
OnlyY:mid 中 “是 y 的倍数但不是 x 的倍数”
Both:mid 中 “既是 x 的倍数又是 y 的倍数”(即 x*y 的倍数)
Neither:mid 中 “既不是 x 的倍数也不是 y 的倍数”
用容斥计算数量(因为 x、y 互质):
both = mid/(x*y)divX = mid/xdivY = mid/yonlyX = divX - both (只能给 y)onlyY = divY - both (只能给 x)neither = mid - divX - divY + both(谁都能去)目标:要给 x 国 cntx 人,给 y 国 cnty 人。
OnlyY + NeitherOnlyX + Neither更关键的是:
OnlyY 这类人 不能去 y,所以如果要派他们,只能派给 x;
OnlyX 这类人 不能去 x,所以如果要派他们,只能派给 y。
因此最自然的贪心/判定方式是:
OnlyY 去满足 x 国需求;不足的部分只能从 Neither 里补:
needFromNeitherForX = max(0, cntx - onlyY)OnlyX 去满足 y 国需求;不足的部分只能从 Neither 里补:
needFromNeitherForY = max(0, cnty - onlyX)那么灵活池 Neither 需要同时填两个缺口,所以总需求:
target = needFromNeitherForX + needFromNeitherForY可行条件就是:
neither >= target代码里:
javaint target = Math.max(0, count_x - mid / y + mid / (x * y)) + Math.max(0, count_y - mid / x + mid / (x * y));
注意:
mid / y - mid / (x*y) 就是 onlyYcount_x - mid / y + mid / (x*y) 等价于 cntx - onlyYcount_y - mid / x + mid / (x*y) 等价于 cnty - onlyX因此 target 就是在算:
x 国缺多少要从 Neither 补 + y 国缺多少要从 Neither 补
接着代码判断:
javaif (mid - mid / x - mid / y + mid / (x * y) >= target) { right = mid; } else { left = mid + 1; }
左边 mid - mid/x - mid/y + mid/(x*y) 正是 neither。
所以这句就是在判断:neither 是否足够填缺口。
随着 k 增大:
onlyX/onlyY/neither 都是非减的(不会变少)[1..k] 能满足,那么 [1..k+1] 也一定能满足(多了一个人可选,不会更差)所以可以二分最小 k。
你这段代码用的全是 int,但题目里:
cntx, cnty 可到 1e9k 也可能接近 2e9x*y 可能接近 30000*30000=9e8,虽然还在 int 正范围内,但在表达式里如果再做别的运算、以及二分的 mid 很大时,很多地方都应该用 long 才稳。尤其这句:
javamid / (x * y)
x*y 这里虽然可能没溢出,但整体逻辑建议全部改 long,包括 left/right/mid/target,否则边界数据下很容易错。
通常标准写法会是:
long left=1, right= (long)2e9 或者更大一点上界long lcm = (long)x*y;/ 用 long 运算如果你愿意,我可以把这段代码改成不溢出、写法更清晰的 long 版(逻辑不变),并顺带解释一下上界 right 怎么设置得更合理。