题目来源
http://acm.hdu.edu.cn/showproblem.php?pid=6627
题意
给你两个数字n和c,然后给你两个序列 ai , n 个 bi,它们元素个数都为n个。
然后要让你求解一个方程
输出所有x的可能的解。
思路
对于每个
∣
a
i
∗
x
+
b
i
∣
|a_i * x + b_i|
∣ai∗x+bi∣
当
x
>
−
b
i
/
a
i
x>-b_i/a_i
x>−bi/ai时,
∣
a
i
∗
x
+
b
i
∣
=
a
i
∗
x
+
b
i
|a_i * x + b_i| = a_i * x + b_i
∣ai∗x+bi∣=ai∗x+bi
当
x
<
−
b
i
/
a
i
x<-b_i/a_i
x<−bi/ai时,
∣
a
i
∗
x
+
b
i
∣
=
−
(
a
i
∗
x
+
b
i
)
|a_i * x + b_i| = -(a_i * x + b_i)
∣ai∗x+bi∣=−(ai∗x+bi)
然后重点在于肯定有个零点,我们根据这n个 a i b i a_i b_i aibi就能获得n个零点,把这个数轴分为n + 1个区间,然后我们一个区间一个区间讨论答案即可。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
const ll INF = 1e18;
struct node{
int a, b;
double x;
int id;
}f[maxn];
bool cmp(node a, node b)
{
return a.x == b.x ? a.id < b.id : a.x < b.x;
}
int n, c;
int A, B;//A,B是累加,有正负
bool flag;//标记是否有无穷多解
queue <node> que;
void solve(double l, double r)
{
if(A == 0 && B == c){//有无穷多解
flag = true;
return ;
}
double ans = (double)(c - B) / A;//x的值
if(ans > l && ans <= r){//如果解在区间里,区间左开右闭
int x = abs(c - B), y = abs(A);//取绝对值
int z;//求最小公约数
if(x == 0) z = y;// 0/1
else z = __gcd(x, y);//最大公约数
node tem;
tem.a = x / z, tem.b = y / z;//化最简
if(ans < 0)//如果ans的值小于0,答案加个负
tem.a *= -1;
que.push(tem);
}
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
while(!que.empty())
que.pop();
scanf("%d %d", &n, &c);
A = 0, B = 0, flag = false;
for(int i = 1; i <= n; ++i)
{
int a, b;
scanf("%d %d", &a, &b);
f[i].a = a, f[i].b = b, f[i].id = i;
f[i].x = -1.0 * f[i].b / f[i].a;
A -= a, B -= b;//从最左边开始,所以假设所有的绝对值去掉后都是负的
}
sort(f + 1, f + 1 + n, cmp);
double l = -INF, r = -INF;
f[n + 1].x = INF;
for(int i = 1; i <= n + 1; ++i)
{
l = r, r = f[i].x;
solve(l, r);
A += 2 * f[i].a, B += 2 * f[i].b;//换区间后,负的变成正的
}
if(flag)
printf("-1\n");
else
{
printf("%d", que.size());
while(!que.empty())
{
node tem = que.front();
que.pop();
printf(" %d/%d", tem.a, tem.b);
}
printf("\n");
}
}
return 0;
}
参考来源
博客
https://blog.csdn.net/qq_41117236/article/details/98600217