算法竞赛进阶指南,48页,二分
本题要点:
1、 本题的关键数据, 所有的坐标点上,最多只有一个数是奇数;
然后计算小于等于某个坐标点 x,含有的所有等差数列 st[i], ed[i], d[i] (1 <= i <= n)出现过的点的总和。
具体某个等差数列st[i], ed[i], d[i], 在小于等于x的坐标点出现的次数:
(min(ed[i], x) - st[i] ) / d[i] + 1;
2、二分所有的坐标点,注意坐标点的最大值 为 2^31 - 1。直接使用 long long,避免麻烦;
找到第一个和为奇数的左边点;
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long LL;
const LL INF = 0x7fffff;
const int MaxN = 200010;
int T, n;
LL st[MaxN], ed[MaxN], d[MaxN];
LL calc_sum(int mid)
{
LL sum = 0;
for(int i = 1; i <= n; ++i)
{
if(mid >= st[i])
{
LL minN = ed[i] < mid ? ed[i] : mid;
sum += (minN - st[i]) / d[i] + 1;
}
}
return sum;
}
void solve()
{
LL L = 1, R = INF + 1, mid;
while(L < R) //找到第一个和为奇数的点
{
mid = (L + R) / 2;
if(calc_sum(mid) & 1)
{
R = mid;
}else{
L = mid + 1;
}
}
if(L == INF + 1) //全部是偶数的情况
{
printf("There's no weakness.\n");
return;
}
LL s = 0;
for(int i = 1; i <= n; ++i)
{
if(L >= st[i] && (L - st[i]) % d[i] == 0)
{
s++;
}
}
printf("%lld %lld\n", L, s);
}
int main()
{
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
{
scanf("%lld%lld%lld", &st[i], &ed[i], &d[i]);
}
solve();
}
return 0;
}
/*
3
2
1 10 1
2 10 1
2
1 10 1
1 10 1
4
1 10 1
4 4 1
1 5 1
6 10 1
*/
/*
1 1
There's no weakness.
4 3
*/