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;
}
posted @ 2021-07-29 20:26  wzyyy  阅读(50)  评论(0)    收藏  举报