洛谷 AT249 紅茶(Tea) - 模拟、二分
洛谷 AT249 紅茶(Tea)
题目链接:洛谷 AT249 紅茶(Tea)
算法标签: 模拟
,二分
题目
题目描述
一天,\(kagamiz\) 一边喝红茶,一边尝试解答如下的问题:
当由两个正整数所组成的正整数组以如下方式排列时, \((m,n)\)是这个数列里的第几组?
\((1,1),(2,1),(1,2),(3,1),(2,2),(1,3),(4,1),(3,2),(2,3),(1,4),(5,1),…\)
这个问题对他来说太简单了,所以他更深入地考虑了以下这个问题:
当上述数列中的 \(i\) 个组为\((a_i,b_i)\),第 \(j\) 个组为\((a_j,b_j)\)时, \((a_i+a_j,b_i+b_j)\)是这个数列里的第几组?
你的任务就是帮助他解答这个问题。
输入格式
输入仅一行,为两个正整数 \(i,j\) 。
输出格式
输出为一个正整数,表示当上述数列中的第\(i\)个组为\((a_i,b_i)\), 第\(j\)个组为\((a_j,b_j)\)时, \((a_i+a_j,b_i+b_j)\)是这个数列里的第几组。
输入输出样例
输入 #1
1 1
输出 #1
5
输入 #2
3 2
输出 #2
13
输入 #3
114 514
输出 #3
1155
说明/提示
数据范围:
- \(i≥1,1≤j≤10^8\),且\(i,j\)均为正整数。
- \(i,j\) 可能相等。
题解:
首先这道题我们要看所给出的数对:
\((1,1),(2,1),(1,2),(3,1),(2,2),(1,3),(4,1),(3,2),(2,3),(1,4),(5,1),…\)
我们可以发现,和为\(n\)的数对共有\(n - 1\)对,所以可以根据这个规律来统计所询问为第几组。
之后继续观察,在每一个合为\(n\)的数对组中,第一位是从 \(n - 1\) 到 \(1\) 递减的,而第二位则正好相反,从 \(1\) 到 \(n - 1\)递增,这个可以统计为第几行(将所有和相等的数对看做一行)的第几个。
同样由于本题的数据范围 \(1 ≤ j ≤ 10^8\) ,显然我们暴力进行查询和求和是很受限制的(没有测过),所以我们选择使用等差数列求和与二分答案的方式进行求解,具体代码实现如下:
long long getsum(ll q) // 等差数列求和
{
return 1ll * ((1 + q) * q) / 2;
}
void find(long long q) // 二分查找答案
{
ll l = 1, r = N + 1, mid;
while(l < r)
{
mid = (l + r) >> 1;
if (getsum(mid) >= q)
r = mid;
else
l = mid + 1;
}
yy = q - getsum(l - 1);
xx = l - yy + 1;
}
AC代码
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
const int N = 1e8 + 10;
typedef long long ll;
int xx, yy;
long long getsum(ll q)
{
return 1ll * ((1 + q) * q) / 2;
}
void find(long long q)
{
ll l = 1, r = N + 1, mid;
while(l < r)
{
mid = (l + r) >> 1;
if (getsum(mid) >= q)
r = mid;
else
l = mid + 1;
}
yy = q - getsum(l - 1);
xx = l - yy + 1;
}
int main()
{
ll x, y;
scanf("%lld%lld", &x, &y);
find(x);
ll ax = xx, ay = yy;
find(y);
ll bx = xx, by = yy;
ll qx = ax + bx, qy = ay + by;
ll pos = qx + qy - 1;
pos = getsum(pos - 1) + qy;
printf("%lld\n", pos);
return 0;
}