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;
}