JZOJ 3637. 【COCI2013】linije

Description

有一天Mirko 和他忠诚的朋友Slavko 感动非常的无聊。他们无聊的结果就是创造出一个新游戏!在游戏的开始,他们在一个坐标系中画下N 个点。玩家轮流开始他们的回合,并且Mirko 首先开始。他画一条直线,平行坐标系其中一个轴,并且经过N 个点中的一个点。在接下来的步骤中,玩家需要画一条平行坐标系一条轴的直线,并且经过N 个点中被上一步对手画下直线经过的点。每条直线不能重复绘制。不能完成自己一局的玩家为输家。

确定谁有必胜策略。

Input

输入的第一行包含正整数N(1<=N<=10000)。

接下来N行每行包含两个整数X和Y,画下的点的坐标(1<=X,Y<=500)。

Output

输出唯一的一行包含必胜玩家的名字,‘Mirko’或‘Slavko’。

Sample Input

输入1:

3

1 1

1 2

1 3

输入2:

4

1 1

1 2

2 1

2 2

Sample Output

输出1:

Mirko

解释:如果Mirko 画直线y=1,那么Slavko 迫不得已要画x=1,然后Mirko 画直线y=2,这样Slavko 只能重新再画直线x=1,这是不被允许的。

输出2:

Slavko

Data Constraint

40%的数据满足N<=10。

分析

我们定义一条有效线 当且仅当他和任意一条坐标轴平行并且经过给出的一点。
线和点可以由二分图表示。图左侧的节点表示与x轴平行的有效线,右侧的节点表示与y轴平行的有效线。对于每个输入点(x,y),我们可以在从左侧的线x和从右侧的线y之间连边。
现在游戏可以这样呈现:在Mirko第一个选择一个节点,并从图中删除它后,Slavko删除任何未删除的节点并且该节点连接到以前删除的节点下一个,Mirko做同样的,他们重复这个动作,当其中一人无法删除任何点时,则该人失败。
由于这与二分图相关,因此预期该解将与最大匹配相关联。
如果存在完美匹配(其中来自图的每个节点与恰好与其共享边缘的另一个节点配对),则Slavko将获胜。否则,Mirko赢了。
为什么?让我们假设在我们的图中有一个完美的匹配。 Mirko选择哪个节点无关紧要 - 它在图的另一侧总是具有现有的匹配,使得Slavko的下一个移动成为可能。因此,无论Mirko选择,Slavko可以反击。这使得Mirko成为指定的失败者。
如果没有完全匹配,我们可以分析最大的一个。 Mirko可以在开始时选择任何不匹配的节点。现在节点Slavko选择肯定会有一对(如果没有一对,我们可以通过连接Mirko和Slavko的节点 - 提出最大匹配 - 矛盾),所以Mirko可以使用与Slavko相同的战术。

代码

#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <cstdio>
#include <cmath> 

#define N 1010
#define MAX 500

using namespace std;

vector <int> E[N];

int now;
int n;
int vis[N];
int x[N],y[N];
int cntX[N],cntY[N];

bool dfs(int dep)
{
    if (dep == -1)
        return 1;
    if (vis[dep] == now)
        return 0;
    vis[dep] = now;
    for (int i = 0; i < E[dep].size(); i++)
    {
        if (dfs(y[E[dep][i]]))
        {
            y[E[dep][i]] = dep;
            x[dep] = E[dep][i];
            return 1;
        }
    }
    return 0;
}

void work()
{
    for (int i = 1; i <= MAX; i++)
    {
        now++;
        if (dfs(i));
    }
}

int main()
{
    freopen("linije.in","r",stdin);
    freopen("linije.out","w",stdout);
    scanf("%d",&n);
    for (int i = 1; i <= n; i++)
    {
        int xn,yn;
        scanf("%d%d",&xn,&yn);
        cntX[xn]++;
        cntY[yn]++;
        E[xn].push_back(yn);
    }

    memset(x,-1,sizeof(x));
    memset(y,-1,sizeof(y));

    work();

    for (int i = 1; i <= MAX ;i++)
    {
        if (cntX[i] > 0 && x[i] == -1 || cntY[i] > 0 && y[i] == -1)
        {
            printf("Mirko\n");
            return 0;
        }
    }
    printf("Slavko\n");
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值