Game on Plane HDU6976
HDU6976
题目大意:
有\(n\)条直线,\(Alice\)会在这\(n\)条直线中选出\(k\)条,\(Bob\)会在\(Alice\)选出的\(k\)条直线基础下额外画出一条直线,我们这样定义对\(Bob\)的惩罚值:设\(Bob\)所画的直线为\(L\),\(Alice\)所选择的直线\(l_i\)若与\(L\)有交点,则\(Bob\)的惩罚值增加\(1\),我们需要计算\(k=1,2,3.......n\)时\(Bob\)的惩罚值,两者都以最优方式选择。
这是典型的博弈问题,我们可以仔细想一想,对于\(Alice\)与\(Bob\)而言,什么才是最优的方式?显然,对于\(Alice\)而言,所选择的线要尽量让\(Bob\)难以画出与大多数线平行的\(L\),而\(Bob\)就要在\(Alice\)所选择的糟糕前提下画出尽量与大多数线平行的线\(L\)。
有一个显而易见的道理,若\(L\)与\(l_i\)无交点,则\(L\)与\(l_i\)平行。考虑\(Alice\)所选择的线集合,可以将集合里线进行分组,将斜率相同的线分在一起,那么对于\(Bob\)而言,显然\(L\)只能与一组线都平行,与其他线都会贡献惩罚值。这样我们就找到了\(Bob\)的最优方式:与集合中所分的元素最多一个组平行,惩罚值就是其他组直线个数。那么对于\(Alice\),\(Alice\)就要尽量平衡各个分组的大小,让各个分组大小类似,尽量不出现“一枝独秀”现象。
具体的,我们可以对n条直线斜率进行排序,从而求出每种斜率直线数量,然后按照一种一条的顺序选择即可。例如,直线斜率为\({1,1,1,2,2,2,3}\),那么我们选择顺序就是\({1,2,3,1,2,1,2,1,2}\)。排序时要注意,两点确定直线斜率时大概率会出现斜率为小数或斜率不存在现象,我们可以将斜率化为最简分数形式即可。保证斜率的唯一性。具体可以参考代码。
我在代码中使用了差分的方式,具体的,对于分组\({3,3,1}\),相当于第一次可以选择\(1,2,3\),下次只能选择\(1,2\)了,可以差分后求前缀和。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<stack>
#include<string.h>
#include<deque>
#include<vector>
#include<map>
#define Lint long long
using namespace std;
typedef pair<int,int> p;
int T,n,x1,y1,x2,y2;
p a[100010];
int num[100010],tot;
int f[100010];
int gcd(int x,int y){
if(!y) return x;
return gcd(y,x%y);
}
int main(){
// freopen("1004.in","r",stdin);
// freopen("a.out","w",stdout);
scanf("%d",&T);
while(T--){
int tot=0;
memset(f,0,sizeof(f));
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
int dx=(x2-x1),dy=(y2-y1);
if(!dx) dy=1;
if(!dy) dx=1;
if(dx<0) dx=-dx,dy=-dy;
int d=gcd(abs(dx),abs(dy));
dx/=d,dy/=d;
a[i]=make_pair(dx,dy);
}
sort(a+1,a+1+n);
int be=1;
for(int i=1;i<=n;++i){
if(a[i]!=a[i+1]){
num[++tot]=i-be+1;
be=i+1;
}
}
sort(num+1,num+1+tot);
for(int i=1;i<=tot;++i){
f[1]++;
f[num[i]+1]--;
}
for(int i=1;i<=n;++i){
f[i]+=f[i-1];
}
for(int i=1,j=1;i<=n;++i){
if(!f[j]) j++;
f[j]--;
printf("%d\n",i-j);
}
}
return 0;
}

浙公网安备 33010602011771号