https://codeforces.com/problemset/problem/985/D
题号 NC112807
名称 Sand Fortress
来源 CF985D
题目描述
给你两个个数n和H,现在要求你求一个序列满足:
- 最左边的一个数的大小不超过H
- 相邻两个数之差的绝对值不超过1
- 所有数之和等于n
求这样一个序列的最小长度是多少?
样例
输入 5 2 输出 3
输入 6 8 输出 3
Here are the heights of some valid castles: n = 5, H = 2, [2, 2, 1, 0, ...], [2, 1, 1, 1, 0, ...], [1, 0, 1, 2, 1, 0, ...] n = 6, H = 8, [3, 2, 1, 0, ...], [2, 2, 1, 1, 0, ...], [0, 1, 0, 1, 2, 1, 1, 0...] (this one has 5 spots occupied) The first list for both cases is the optimal answer, 3 spots are occupied in them. And here are some invalid ones: n = 5, H = 2, [3, 2, 0, ...], [2, 3, 0, ...], [1, 0, 2, 2, ...] n = 6, H = 8, [2, 2, 2, 0, ...], [6, 0, ...], [1, 4, 1, 0...], [2, 2, 1, 0, ...]
算法
(二分)
显然如果序列中有0的话这个序列的长度可以无限大,
所以长度越长就越能找到合法的序列,这样似乎就有两段性,我们可以二分序列的长度
接着我们思考check函数怎么写
假设当前二分的长度是,
我们思考所有长度为的序列中,数字之和最大
是多少
我们分两种情况来讨论:
如图:
sum:
(1) 当 (H + x) % 2 == 1
如图:
sum:$h * h + h - (H * H - H) / 2 ,h = (x + H - 1) / 2 $
(2) 当 (H + x) % 2 == 0
如图:
sum:
得到sum后我们给出一个结论:
有了这个结论只要给出x求出sum,sum >= n返回true (答案小于等于x),sum < n返回false (答案大于x)
下面给出证明:
,显然成立
我们给出一个操作可以得到一个长度为
的数字之和为
的合法序列:
每一次我们将长度为
数字之和为
序列中的最大数字减一(如果有多个最大值任选一个减1)直到sum减少到n
我们分类讨论一下:
- 如果最大的数字在最左端,这个数字减1后依然小于H并且他右边的数字一定是小于等于它的减1后相差的绝对值依然是小于等于1
- 最大的数字不在最左端,它两边的数字也一定是小于等于它的,减1后相差的绝对值依然是小于等于1
所以如果长度为的序列中,数字之和最大
>= n那么一定能构造出一个长度为
的数字之和为n的合法序列
*本题主要关注一下__int128的输入输出
时间复杂度 &preview=true)
参考文献
C++ 代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <unordered_map>
#include <map>
#include <vector>
#include <queue>
#include <set>
#include <bitset>
#include <cmath>
#define P 131
#define lc u << 1
#define rc u << 1 | 1
using namespace std;
__int128 n,H;
__int128 read()
{
__int128 x = 0,f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
inline void write(__int128 x)
{
if(x < 0)
{
putchar('-');
x = -x;
}
if(x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
bool check(__int128 x)
{
if(H >= x)
{
return n <= x * (x + 1) / 2;
}else
{
__int128 h;
if((H + x) % 2 == 0)
{
h = (x + H) / 2;
return n <= h * h - (H * H - H) / 2;
}else
{
h = (x + H - 1) / 2;
return n <= h * h + h - (H * H - H) / 2;
}
}
}
void solve()
{
n = read(),H = read();
__int128 l = 1,r = 1e18;
while(l < r)
{
__int128 mid = l + r >> 1;
if(check(mid)) r = mid;
else l = mid + 1;
}
write(l);
puts("");
}
int main()
{
/*#ifdef LOCAL
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#else
#endif // LOCAL*/
int T = 1;
//init();
//scanf("%d",&T);
while(T --)
{
// scanf("%lld%lld",&n,&m);
solve();
// test();
}
return 0;
}
京公网安备 11010502036488号