题意
给你一堆点,石子在给出的第一个点,两个人互相移动石子,要求每一次移动的距离大于上一次的距离,
思路
博弈论,比赛前由于刚学的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");
}
}