HDU 0708暑假集训队选拔赛1009

HDU 0708暑假集训队选拔赛1009
好像是广西的省赛题目,不确定。
在这里插入图片描述在这里插入图片描述
题意:第一行数据是t,代表t组数据。
每组数据的第一行两个数字是n和m,n代表有n个目标,m代表最少需要拿到的分数。
接下来的n行是n个目标的参数,每个目标是一条直线和一个权值,每个目标的输入是五个数字,前四个是起点的x与y值以及终点的x与y值,第五个代表的是这个目标的价值。
我们要做的是选择一个角度射出一颗子弹,命中一个目标(击中起点与终点也算是击中)就可以获得那个目标的价值,题目要求的是最少前面几个目标出现时就可以达到最少的m的分数。如果不论怎么样都不能达到那个m的分数,就输出-1.


思路:我的第一题计算几何的题目,因为知道了所有的线段的数量求最小的符合条件的数,所以很自然而然地想到了二分。二分的思路确定后就是判定是否可行的函数的写法了。
可以将所有的线段读入时分成起点和终点,用结构体存储每一个点的信息,然后对其进行排序,排出的顺序是按所有的点的逆时针的顺序排序,然后从x正半轴开始向后面扫描,check函数中一旦扫描的总和sum大于等于m就可以return true了,否则就最后return false。
对于点的排序考虑到逆时针方向排序,首先得考虑象限,按逆时针的顺序将四个象限标号为0,1,2,3,象限符号小的点一定是在前面,如果是同一象限的两个点的排序就得看它们的斜率,计算一下可以用x1y2与x2y1的大小来确定谁的斜率小,确定出谁在逆时针方向的前面与后面,如果两个点的斜率也恰好一样,那么起点必须得排在终点的前面,因为check函数中得先加上起点那条线段的权值再减去终点那条线段的权值,所以必须满足先加后减。
对于刚好起点的那些线,一开始就用duan数组来标记,遍历时将它们先加到sum里面,每一次check时的vis数组记录是否正在线上,如果vis为1时遇到了线上的另一个端点说明就得减去其w值并且将vis变为0;如果vis为0时遇到了线上的一个点,说明第一次遇到这条线段,得加上这个线段的w权值。

AC代码:

#include<bits/stdc++.h>
#define INF 0x3F3F3F3F
#define endl '\n'
#define pb push_back
#define css(n) cout<<setiosflags(ios::fixed)<<setprecision(n); 
#define sd(a) scanf("%d",&a)
#define sld(a) scanf("%lld",&a)
#define m(a,b) memset(a,b,sizeof a)
#define p_queue priority_queue
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
ll n,m;
int t;
ll w[maxn];
int vis[maxn];
int duan[maxn];
struct node
{
	int x,y;
	int type;
	int end;
	int id;
	void get_type()
	{
	if (y>=0 && x>0) type=0;
		else if (x<=0 && y>0) type=1;
		else if (x<0 && y<=0) type=2;
		else type=3;//0,1,2,3逆时针转的象限。 
	}
 }dian[maxn<<2];
 
ll det(int ax,int ay,int bx,int by)
{
	ll ans=(ll)ax*by-(ll)bx*ay;
	return ans;
}
bool comp(node a,node b)
{
	if(a.type!=b.type) return a.type<b.type;
	else
	{
		ll kk=det(a.x,a.y,b.x,b.y);
		if(kk!=0)
		{
			return kk>0;//同一象限中det大于0说明前面的点斜率小。 
		 } 
		 else return a.end<b.end;
	}
 } 
bool check(int x)
{
	ll sum=0;
	for(int i=1;i<=x;i++) vis[i]=0;
	for(int i=1;i<=x;i++)
	{
		if(duan[i])
		{
			sum+=w[i];
			vis[i]=1;
		}
	}
//	cout<<sum<<"..."<<endl;
	if(sum>=m) return 1;
	for(int i=1;i<=2*n;i++)
	{
		if(dian[i].id>x) continue;
		int zz=dian[i].id;
		if(vis[zz])
		{
			sum-=w[zz];
			vis[zz]=0;
		}
		else
		{
			sum+=w[zz];
			vis[zz]=1;
		}
		if(sum>=m) return 1;
	}
//	cout<<sum<<",,,"<<endl;
	 return 0;
}
int main()
{
	sd(t);
	while(t--)
	{
		scanf("%lld%lld",&n,&m);
		for(int i=1;i<=n;i++)
		duan[i]=0;
		for(int i=1;i<=n;i++)
		{
			scanf("%d%d%d%d%lld",&dian[i*2-1].x,&dian[i*2-1].y,&dian[i*2].x,&dian[i*2].y,&w[i]);
			dian[i*2-1].get_type();
			dian[i*2].get_type();
			dian[i*2-1].id=dian[i*2].id=i;
			if(det(dian[i*2-1].x,dian[i*2-1].y,dian[i*2].x,dian[i*2].y)<0) swap(dian[i*2-1],dian[i*2]);
			dian[i*2-1].end=0;
			dian[i*2].end=1;
			if(dian[i*2-1].type>dian[i*2].type) duan[i]=1;
		}
		sort(dian+1,dian+1+2*n,comp);
		int l=1;
		int r=n;
		int flag=0;
		while(l<r)
		{
			int mid=(l+r)>>1;
			if(check(mid))
			{
				flag=1;
				r=mid;
		//		cout<<mid<<"---"<<flag<<endl;
			}
			else l=mid+1;
		}
		if(!flag) printf("-1\n");
		else printf("%d\n",l);
	 } 
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值