一、题目大意
我们有N个数列,每个数列可以用X,Y,Z三个数字代表,数列的元素为 X,X+Z,X+2*Z,X+K*Z<=Y,题目保证这个N个数列中的每个元素出现的次数都为偶数次(即第1个序列中的某个元素,一定会在其他的数列中出现,并且出现次数是偶数)
但多次偏差之后,N个数列中存在一个元素,在所有数列中出现的次数为奇数次,题目让我们找到这个元素和它出现的次数,如果所有元素都出现了偶数次,那么输出no corruption。
二、思路
对这个元素使用二分即可,left选择-1,right选择最大的Y+1
二分的依据如下
不难看出,当一个数字不属于任何一个数列时
它在所有数列中出现的次数就是0,也是一个偶数
当一个数字属于其中某一个数列时
根据题意,它一定也在其他的数列出现,出现的次数和也会是偶数
如果所有数列中每一个元素出现的次数都是偶数次
那么所有数列中元素的个数之和也一定是偶数
但是如果其中有且仅有一个元素出现了奇数次
那么所有元素的数量之和也一定是奇数
假如我们对元素的右边界进行二分枚举
当mid小于那个出现次数为奇数的元素时
所有数列中小于等于mid的元素个数一定为偶数
当mid大于等于那个出现次数为奇数的元素时
所有数列中小于等于mid的元素个数一定为奇数
因此不断的二分,一个极限limit时
当mid=limit,所有数列小于等于mid的元素数量为偶数
当mid=limit+1,所有数列小于等于mid的元素数量为奇数
那么这个limit+1,就是那个唯一的差错值,也是本题目的答案
原理就是,多个偶数相加也一定时偶数,但只有其中有一个奇数
那么最终的和也一定是一个奇数
所以通过二分,可以不断的接近,迅速找到一群偶数中的唯一奇数
本题目的输入也比较特殊,我在编写输入函数上也花费了一些时间,最终的思路一个字符一个字符的读入,就是对回车符号、空格和数字区分处理,在这里不再赘述
三、代码
#include <iostream>
using namespace std;
typedef long long ll;
int n;
char str[128];
ll x[100007], y[100007], z[100007], maxt;
bool judge(ll mid)
{
ll count = 0;
for (int i = 1; i <= n; i++)
{
if (mid < x[i])
{
continue;
}
ll right = min(y[i], mid);
ll currentCount = (right - x[i]) / z[i];
currentCount++;
currentCount = currentCount % 2;
count += currentCount;
}
return count % 2 == 0;
}
void findMax()
{
maxt = 0;
for (int i = 1; i <= n; i++)
{
maxt = max(maxt, y[i]);
}
}
ll findCount(ll mid)
{
ll count = 0;
for (int i = 1; i <= n; i++)
{
if (mid < x[i] || mid > y[i])
{
continue;
}
if ((mid - x[i]) % z[i] == 0)
{
count++;
}
}
return count;
}
void binarySearch()
{
findMax();
ll left = -1, right = maxt + 1;
while (left + 1 < right)
{
ll mid = (left + right) / 2;
if (judge(mid))
{
left = mid;
}
else
{
right = mid;
}
}
if (right == maxt + 1)
{
printf("no corruption\n");
}
else
{
printf("%lld %lld\n", right, findCount(right));
}
}
bool isNumber(char c)
{
char number[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
for (int i = 0; i < 10; i++)
{
if (c == number[i])
{
return true;
}
}
return false;
}
void resetArray()
{
// 多处理一位,多一份安全
for (int i = 0; i <= n + 1; i++)
{
x[i] = 0;
y[i] = 0;
z[i] = 0;
}
}
int main()
{
n = 1;
char c;
// 接收到换行符的数量
int enterCount = 0, mode = 1;
while (~scanf("%c", &c))
{
if (isNumber(c))
{
// 始于数字
enterCount = 0;
switch (mode)
{
case 1:
x[n] = x[n] * 10 + (c - '0');
break;
case 2:
y[n] = y[n] * 10 + (c - '0');
break;
case 3:
z[n] = z[n] * 10 + (c - '0');
break;
default:
break;
}
}
else if (c == ' ')
{
// 如果是空格,代表开始输入下一个数字
mode++;
}
else if (c == '\n')
{
enterCount++;
// 如果是回车,单独的一个回车,那么n自增
if (enterCount == 1)
{
n++;
mode = 1;
}
else if (n > 1)
{
// 如果第n行的x为0.那么n取大了
if (x[n] == 0)
{
n--;
}
// 多个回车,并且当前有数据,直接处理
if (n > 1)
{
binarySearch();
resetArray();
n = 1;
}
}
}
}
// 输入结束,如果n还有值,那么在处理一次
if (x[n] == 0)
{
n--;
}
if (n > 1)
{
binarySearch();
}
return 0;
}