cf 936B Sleepy Game

本文介绍了一个游戏策略问题,玩家在一个有向图上轮流移动棋子,目标是使对手无法行动而取胜或至少保持不败。文章提供了算法思路和C++代码实现,通过构建辅助图和深度优先搜索来解决此问题。

一 原题

B. Sleepy Game
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

Petya and Vasya arranged a game. The game runs by the following rules. Players have a directed graph consisting of n vertices and medges. One of the vertices contains a chip. Initially the chip is located at vertex s. Players take turns moving the chip along some edge of the graph. Petya goes first. Player who can't move the chip loses. If the game lasts for 106 turns the draw is announced.

Vasya was performing big laboratory work in "Spelling and parts of speech" at night before the game, so he fell asleep at the very beginning of the game. Petya decided to take the advantage of this situation and make both Petya's and Vasya's moves.

Your task is to help Petya find out if he can win the game or at least draw a tie.

Input

The first line of input contain two integers n and m — the number of vertices and the number of edges in the graph (2 ≤ n ≤ 1050 ≤ m ≤ 2·105).

The next n lines contain the information about edges of the graph. i-th line (1 ≤ i ≤ n) contains nonnegative integer ci — number of vertices such that there is an edge from i to these vertices and ci distinct integers ai, j — indices of these vertices (1 ≤ ai, j ≤ nai, j ≠ i).

It is guaranteed that the total sum of ci equals to m.

The next line contains index of vertex s — the initial position of the chip (1 ≤ s ≤ n).

Output

If Petya can win print «Win» in the first line. In the next line print numbers v1, v2, ..., vk (1 ≤ k ≤ 106) — the sequence of vertices Petya should visit for the winning. Vertex v1 should coincide with s. For i = 1... k - 1 there should be an edge from vi to vi + 1 in the graph. There must be no possible move from vertex vk. The sequence should be such that Petya wins the game.

If Petya can't win but can draw a tie, print «Draw» in the only line. Otherwise print «Lose».

Examples
input
Copy
5 6
2 2 3
2 4 5
1 4
1 5
0
1
output
Win
1 2 4 5 
input
Copy
3 2
1 3
1 1
0
2
output
Lose
input
Copy
2 2
1 2
1 1
1
output
Draw
Note

In the first example the graph is the following:

Initially the chip is located at vertex 1. In the first move Petya moves the chip to vertex 2, after that he moves it to vertex 4 for Vasya. After that he moves to vertex 5. Now it is Vasya's turn and there is no possible move, so Petya wins.

In the second example the graph is the following:

Initially the chip is located at vertex 2. The only possible Petya's move is to go to vertex 1. After that he has to go to 3 for Vasya. Now it's Petya's turn but he has no possible move, so Petya loses.

In the third example the graph is the following:

Petya can't win, but he can move along the cycle, so the players will draw a tie.


二 分析

题意:甲乙两人在简单有向图(最多10^5个顶点,2*10^5条边)上玩一个游戏,开始时指定一个顶点作为起点,每一轮一个玩家从当前顶点出发,沿着一条边达到一个新的顶点,轮流进行,一个玩家无路可走时就算失败,进行10^6轮后仍未分胜负算平。你先手,并且可以指定后手玩家的操作,问是否可以必胜,如果不行,是否可以逼和。


分析:和局的充要条件是显然的:图中有从起点可达的环。获胜条件其实是找到一条长度为奇数的路径,终点是一个出度为0的点。把原图中每个点S拆分为S0和S1,如果原图中有一条边(S, T),则连接(S0, T1)和(S1, T0)。这样我们只要从起点S1做一次DFS,如果能找到出度为0的点T0,就找到了一条必胜路径。


三 代码

之后都按照这个代码风格来了 函数名首字母大写 变量全部小写 控制语句关键字(if/for/while)后加空格

/*
AUTHOR: maxkibble
LANG: c++ 17
PROB: cf 936B
*/

#include <cstdio>
#include <vector>
#include <queue>

using namespace std;

#define pb push_back

const int maxn = 1e5 + 10;

int n, m, d[maxn];
vector<int> g1[maxn], g2[maxn << 1], trace;
short vis[maxn << 1];

void Init() {
    scanf("%d%d", &n, &m);
    int c, t;
    for (int i = 1; i <= n; i++) {
        scanf("%d", &c);
        while(c--) {
            scanf("%d", &t);
            g1[i].pb(t);
            d[t]++;
        }
    }
}

void BuildGraph() {
    for (int i = 1; i <= n; i++) {
        for (int t: g1[i]) {
            g2[i * 2 - 1].pb(t * 2);
            g2[i * 2].pb(t * 2 - 1);
        }
    }
}

bool Dfs1(int x) {
    if (vis[x]) return false;
    vis[x] = 1;
    trace.pb(x);
    if (x % 2 == 1 && g2[x].size() == 0) return true;
    for (int y: g2[x]) {
        if (Dfs1(y)) return true;
    }
    trace.pop_back();
    return false;
}

bool Dfs2(int x) {
    if (vis[x] == 2) return true;
    vis[x] = 2;
    for (int y: g1[x]) {
        if (Dfs2(y)) return true;
    }
    vis[x] = 1;
    return false;
}

int main() {
    Init();
    BuildGraph();
    
    int pos;
    scanf("%d", &pos);
    
    if (Dfs1(pos * 2)) {
        puts("Win");
        for(int item: trace) printf("%d ", (item + 1) >> 1);
        return 0;
    }
    
    if (Dfs2(pos)) puts("Draw");
    else puts("Lose");

    return 0;
}


使用c++完成我给出的题目 写完代码之后单独说明思路,代码风格要求:变量名不超过4个字符,请注意:全部小写,这能帮助你拿到更高的分数!!!!!!,不用三路表达式,去掉注释,使用~~~cpp ~~~ 格式,使用万能头和using namespace std,typedef long long ll,if和for后都要加大括号和换行,除了循环变量以外尽量多的使用全局变量,少使用动态内存: #### 问题陈述 有一个行数为 $H$ 列数为 $W$ 的网格。我们将位于从上往下 $i$ 行,从左往上 $j$ 列的单元格称为单元格 $(i,j)$ 。每个单元格上最多有一面镜子。 高桥站在 $(1,1)$ 单元格的左边,青木站在 $(H,W)$ 单元格的右边。高桥拿着手电筒,从 $(1,1)$ 单元格的左侧向右侧照射。在此,假设手电筒的光线没有漫射,是一条直线传播的光线。 高桥的目标是通过网格中的镜子将手电筒的光线传递给青木。 镜子的位置有三种。当光线照射到镜子上时,光线的方向会根据镜子的位置发生变化。对于每种镜面位置,每个入射方向的出射方向如下图所示。 - 类型 A(未放置反射镜) ![](https://img.atcoder.jp/abc431/9a3821cb76d936b95b6979e084d56994.png) - B 型(在连接左上角和右下角的对角线上放置一面镜子) ![](https://img.atcoder.jp/abc431/283ea6eabada389e76e89518fcb9fb18.png) - C 型(在连接右上角和左下角的对角线上放置一面镜子) ![](https://img.atcoder.jp/abc431/9410773fd7615321f27f070d5f0b1844.png) 网格上镜子的位置由长度为 $W$ 的 $H$ 字符串表示: $S_1,S_2,\ldots,S_H$ .当 $S_i$ 的 $j$ -th 字符为 "A "时,单元格 $(i,j)$ 为 A 类型;当为 "B "时,单元格 $(i,j)$ 为 B 类型;当为 "C "时,单元格 $(i,j)$ 为 C 类型。 高桥可以执行以下任意次数的操作,将光线传送给青木: - 选择一个单元格,并将该单元格的镜面位置改为不同的类型。 求将光传送给青木所需的最少操作次数。 给你 $T$ 个测试案例,请找出每个案例的答案。 #### 限制因素 - $1\leq T$ - $1\leq H,W$ - $HW\leq 2\times 10^5$ - $S_i$ 是长度为 $W$ 的字符串,由 `A`、`B`、`C` 组成。 - $T$ 、 $H$ 和 $W$ 是整数。 - 所有测试用例中 $HW$ 的总和最多为 $2\times 10^5$ 。 #### 输入样本 1 ``` 4 3 4 ABCB CACC BCBA 2 2 CB AA 1 10 BCBCBCBCBC 10 10 CCAABACAAA CCCBACACCA BACAABCBBA ACCCAACCCA CCAAAACCBA AACBBACCAA BCCCACBBAB CBBCAACCCC CBBCCBCBCA BBACABBACC ``` ### 样本输出 1 ``` 0 2 10 5 ``` 在第一个测试案例中,无需执行任何操作即可将光线传送到青木。 ![](https://img.atcoder.jp/abc431/f66a190627b09b9e6c4cbacb84b6d9bd.png) 对于第二个测试案例,如下图所示,将单元格 $(1,1)$ 的镜像位置改为 A 型,将单元格 $(2,2)$ 的镜像位置改为 B 型,就可以将光线传送给青木。不可能通过一次或更少的操作将光线传送给青木,因此答案为 $2$ 。 ![](https://img.atcoder.jp/abc431/1aeac5f2fecf4319d369b8cc3067959a.png)
最新发布
11-09
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值