题目大意
A l i c e Alice Alice和 B o b Bob Bob打球,两个人轮流发球。每一轮以某一方胜利宣告结束。接球方胜利,则称为“打破发球”;发球方胜利,则称为“保持发球”。
已知 A l i c e Alice Alice赢了 a a a轮, B o b Bob Bob赢了 b b b轮,但不清楚谁先发球,也不清楚谁赢了哪轮游戏。让 k k k表示“打破发球”的次数,现在请你求出 k k k的所有可能取值。
输入描述
每次测试包含多组测试用例。
第一行一个 t ( 1 ≤ t ≤ 1 0 3 ) t(1≤t≤10^3) t(1≤t≤103)表示测试用例的数目,接下来t行每行一个测试用例包含 a a a和 b ( 0 ≤ a , b ≤ 1 0 5 ; a + b > 0 ) b(0≤a,b≤10^5;a+b>0) b(0≤a,b≤105;a+b>0)分别表示 A l i c e Alice Alice和 B o b Bob Bob获得胜利的游戏轮数。
保证所有测试用例的 a + b a+b a+b之和不超过 2 × 1 0 5 2×10^5 2×105。
输出描述
对每组测试用例输出两行。
第一行一个 m ( 1 ≤ m ≤ a + b + 1 ) m(1≤m≤a+b+1) m(1≤m≤a+b+1)表示 k k k的所有可能取值有多少种。
第二行输出 m m m个不同的整数 k 1 , k 2 , … k m ( 0 ≤ k 1 < k 2 < … < k m ≤ a + b ) k_1,k_2,…k_m(0≤k_1<k_2<…<k_m≤a+b) k1,k2,…km(0≤k1<k2<…<km≤a+b),表示按照升序排列的所有 k k k的可能取值。
样例输入
3
2 1
1 1
0 5
样例输出
4
0 1 2 3
2
0 2
2
2 3
思路
假设 A l i c e Alice Alice先发球,一共进行了 a + b a+b a+b轮游戏。如果 a + b a+b a+b是偶数,那么两人都发了 a + b 2 \frac{a+b}{2} 2a+b轮球。如果 a + b a+b a+b是奇数,那么 A l i c e Alice Alice比 B o b Bob Bob多发一轮球。将两种情况整合成数学语言,那么 A l i c e Alice Alice发了 p = ⌈ a + b 2 ⌉ p=⌈\frac{a+b}{2}⌉ p=⌈2a+b⌉轮球, B o b Bob Bob发了 q = ⌊ a + b 2 ⌋ q=⌊\frac{a+b}{2}⌋ q=⌊2a+b⌋轮球。
假设 B o b Bob Bob“打破发球”的轮数是 x ( 0 ≤ x ≤ p ) x(0≤x≤p) x(0≤x≤p), A l i c e Alice Alice“打破发球”的轮数是 y ( 0 ≤ y ≤ q ) y(0≤y≤q) y(0≤y≤q),也就是说: B o b Bob Bob作为接球方赢了 x x x轮,作为发球方赢了 b − x b-x b−x轮; A l i c e Alice Alice作为接球方赢了 y y y轮,作为发球方赢了 a − y a-y a−y轮。因此, B o b Bob Bob作为接球方输了 a − y a-y a−y轮, A l i c e Alice Alice作为接球方输了 b − x b-x b−x轮。已知 B o b Bob Bob接了 p p p轮球, A l i c e Alice Alice接了 q q q轮球,对于固定的某一方,无论是接球还是发球只有输赢两种状态,我们得到方程: x + ( a − y ) = p x+(a-y)=p x+(a−y)=p, y + ( b − x ) = q y+(b-x)=q y+(b−x)=q,变形得 x − y = p − a = b − q x-y=p-a=b-q x−y=p−a=b−q。由于 p − a = b − q p-a=b-q p−a=b−q,我们任取 x − y = p − a x-y=p-a x−y=p−a,得约束方程 y = x + ( a − p ) ( 0 ≤ x ≤ p , 0 ≤ y ≤ q ) y=x+(a-p)(0≤x≤p,0≤y≤q) y=x+(a−p)(0≤x≤p,0≤y≤q)。只需要求出所有满足约束方程的 ( x , y ) (x,y) (x,y),再代入 k = x + y k=x+y k=x+y即可。
当然,上面给出的是 A l i c e Alice Alice先发球的情况, A l i c e Alice Alice获胜的轮数为 a a a,发了 p p p轮球。对于 B o b Bob Bob先发球的情况, B o b Bob Bob仍发了 p p p轮球,获胜的轮数却为 b b b,因此我们只需交换 a , b a,b a,b的值,再重复一次上面的步骤即可。
考点
数学模型的建立
约束方程的推导
AC代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;
int T, a, b, p, q, k[maxn], vis[maxn], ans;
void init() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> T;
}
int y(int x) {
return x + a - p;
}
void pd() {
for (int i = 0; i <= p; i++) {
if (y(i) >= 0 && y(i) <= q) {
if (vis[i + y(i)] == 0)ans++, k[ans] = i + y(i), vis[i + y(i)] = 1;
}
}
}
int main()
{
init();
while (T--) {
cin >> a >> b;
p = ceil((a + b)*1.0 / 2);
q = (a + b) / 2;
ans = 0;
memset(vis, 0, sizeof(vis));
pd();
swap(a, b);
pd();
cout << ans << endl;
sort(k + 1, k + 1 + ans);
for (int i = 1; i <= ans; i++) {
cout << k[i] << " ";
}
cout << endl;
}
return 0;
}
心得
t o u r i s t t\color{red}ourist tourist果然是世界第一啊,他出的这道 B B B题实在是让我痛心疾首,签出个 A A A后就罚了两小时坐。一开始我是尝试去找规律,以为是一道构造题,结果在纸上画了半天啥也推导不出来。比赛最后我干脆直接贪心过样例,没想到试的那种规律还真和最后的结论差不多 ( ( (见 t o u r i s t t\color{red}ourist tourist的题解 ) ) )。赛后看题解才发现这道题用的是宏观的思想,类似建立数学模型,得到数学方程吧,最后和线性规划差不多,只是用计算机去求出所有的解。
这题不会超时,因为是所有测试用例中的 a + b a+b a+b之和不会超过 2 × 1 0 5 2×10^5 2×105,枚举是 O ( n ) O(n) O(n)的,排序是 O ( n l o g n ) O(nlogn) O(nlogn)的,只需要推导出中间结论。按照 t o u r i s t t\color{red}ourist tourist题解最后的结论,我还真差不多推导出来了,用了一下午。不过完全没必要,毕竟 t o u r i s t t\color{red}ourist tourist本人都没给出证明过程。考场上是绝对没有时间推导出最终结论的。