HDU(6627) equation

传送门

题解:

该题可以将 ∑|ai⋅x+bi|=C (i=1,2,……,N)看成 F(x)= ∑|ai⋅x+bi|(i=1,2,……,N)这样就构造出一个函数F(x);

利用高中分段函数知识,对于每个绝对值都具有对应的分段点,该分段点即为 -bi/ai;

因此N个分段点可将这一整个函数串最多分成N+1段;

我们知道对于每个分段点(必须是从小到大排序后的):左端去掉绝对值符号不加负号,右端去掉绝对值符号加负号。这样我们可以排序,枚举这些分段点,对于每个分段点来去掉绝对值符号  算一次X;并判对算出的X是否符合要求。

关于X的要求:

1.算出的X,是不是在在两个相邻分段点内(第一个分段点的左端点在负无穷,最后一个分段点的右端点在正无穷)。

2.对于数组 a[] 的和 是否为0;若数组 a 的和为0,需要判断数组 b 的值是否为C,是的话说明X取值是谁都行。

对于最后的打印结果:

1.相同数不需要打印,并且从小到大输出,很自然想到set容器。

2.打印结果要求是最简的分数,所以要除以gcd;

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
struct node
{
	int a,b;
	double zz;
	bool operator < (const node x)const
	{
		return zz<x.zz;
	}
}pp[100005];

int gcd(int x,int y)
{
	return y>0?gcd(y,x%y):x; 
}

signed main()
{
	int T;
	scanf("%lld",&T);
	while(T--)
	{
		int N,C;
		set <struct node> ans;
		int suma=0,sumb=0;
		scanf("%lld %lld",&N,&C);
		for(int i=1;i<=N;i++)
		{
			scanf("%lld %lld",&pp[i].a,&pp[i].b);
			suma-=pp[i].a;
			sumb-=pp[i].b;
			pp[i].zz=-1.0*pp[i].b/pp[i].a;
		}
		sort(pp+1,pp+N+1);
		bool ff=false;
		for(int i=0;i<=N&&!ff;i++)
		{
			if(i>0)
			suma=suma+2*pp[i].a,sumb=sumb+2*pp[i].b;
			if(suma==0)
			{
				if(sumb==C)
				{
					ff=true;
					continue;
				}
			}
			else
			{
				double x=1.0*(C-sumb)/suma;
				if(i==0&&x<=pp[1].zz||i!=N&&i!=0&&x>pp[i].zz&&x<=pp[i+1].zz||i==N&&x>pp[i].zz)
				{
					struct node uu;
					int g=gcd(abs(C-sumb),abs(suma));
					uu.a=(C-sumb)/g;
					uu.b=suma/g; 
					uu.zz=uu.a*1.0/uu.b;
					if(uu.zz>0)uu.a=abs(uu.a),uu.b=abs(uu.b);
					else
					{
						if(uu.b<0)uu.a*=-1,uu.b*=-1;
					}
					ans.insert(uu);
				}
			}
		}
		if(ff)printf("-1\n");
		else
		{
			if(ans.size()==0)printf("0\n");
			else
			{
				printf("%lld",ans.size());
				set <struct node>::iterator iter=ans.begin();
				while(iter!=ans.end())
				{
					printf(" %lld/%lld",(*iter).a,(*iter).b);
					iter++;
				}
				printf("\n");
			}
		}
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值