洛谷 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
", pos);
return 0;
}