2020杭电多校7 hdu6850 Game

传送门

题意

给你一堆点,石子在给出的第一个点,两个人互相移动石子,要求每一次移动的距离大于上一次的距离,

思路

博弈论,比赛前由于刚学的SG函数,没太会应用,一直在考虑点结束,其实看了题应该明白这道题是边结束。
首先可以建立一个完全图,边权值为两点的距离。考虑最长边,如果一个人选到了这条边,则下一个人必胜。然后我们考虑次长边……这样即把问题分割成了一个子问题。
每次选取最长边,然后把该边两点标记,再选取图中下一条非标记点的最长边。如果在此过程中标记了1,意味着从1开始的先手可以选当标记了1的最长边,必胜。由于这种取法至多只剩下一个点未被标记,因为点数大于1时,意味则这两点间距离可选。所以如果最后只剩下1未被选,则意味着先手无论选哪个点,后手都能选到该点的最长边,必败。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <algorithm>
#include <cmath>
#include <vector>
#include <map>
using namespace std;
#define inf 0x3f3f3f3f
#define ll long long
#define maxn 2005
#define mod 998244353
struct node{
    ll x,y;
}a[maxn];
struct node1{
    ll x,y,r;
}dis[maxn*maxn];
bool cmp(node1 x,node1 y){
    return x.r>y.r;
}

int main(){
    int i, j, k, t, n;
    scanf("%d",&t);
    while(t--){
        int cur=0;
        scanf("%d",&n);
        int vis[maxn]={0};
        for(i=1;i<=n;i++){
            scanf("%lld%lld",&a[i].x,&a[i].y);
            for(j=1;j<i;j++){
                ll r=(a[j].x-a[i].x)*(a[j].x-a[i].x)+(a[j].y-a[i].y)*(a[j].y-a[i].y);
                dis[++cur].r=r;
                dis[cur].x=j;
                dis[cur].y=i;
            }
        }
        sort(dis+1,dis+1+cur,cmp);
        for(i=1;i<=cur;i++){
            ll x=dis[i].x,y=dis[i].y,r=dis[i].r;
            if(vis[x]||vis[y]) continue;
            j=i+1;
            vis[x]=vis[y]=i;
            while(j<=cur&&r==dis[j].r){
            	if((!vis[dis[j].x]||vis[dis[j].x]==i)&&(!vis[dis[j].y]||vis[dis[j].y]==i)){
            		vis[dis[j].x]=vis[dis[j].y]=i;
                	
				}
                j++;
            }
            i=j-1;
            if(vis[1]) break;
        }
        if(vis[1]) printf("YES\n");
        else printf("NO\n");
    }
}