题解:CF936B Sleepy Game
题意
原题意较为复杂。注意到走到出度为
给定起点
S ,构造一条终点出度为0 且长度为奇数的路径。
思路
考虑无解的情况。稍微思考一下可以发现,无解时必须满足不存在任意一条长度为奇数的路径且不满足平局的条件。
问题来到如何判断起点到终点是否有长度为奇数的路径。
这个 trick 来源于 P5663 [CSP-J 2019] 加工零件。
维护
考虑如何转移。对于一条
接下来思考如何平局。平局是好判的,如果我们能在图上转圈圈则可以平局。
形式化地说:如果从起点可以走到一个点数大于
这个性质可能会让你往 tarjan 强连通分量缩点的方面上去想,但是题目对于缩点后的图处理较为困难,所以需要使用其他方法代替。
首先 dfs 染色将
建子图这一部分是较为重要的,因为拓扑排序中一个环上的点是一定不会入队的,所以这些点指向的点也就不会被遍历到,从而导致一些问题。(WA on #6 #29 #31 #45)
排除上面两种情况,剩下来的就是有解的部分。
题目要求构造路径,则在求最短路时记录前驱即可。
注意在寻找路径时,起点(
代码:
#include <bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
const int N = 1e5 + 5;
int n, m, S, T, u, tot, ans[N], deg[N], Deg[N], dis[N][2], pre[N][2];
bool vis[N];
queue<int> q;
vector<int> g[N], G[N];
inline void dfs(int x) {
vis[x] = true;
for(auto u : g[x])
if(! vis[u]) dfs(u);
return ;
}
inline bool toposort() {
for(int i = 1 ; i <= n ; ++ i)
if(vis[i] && Deg[i] == 0) q.push(i);
while(! q.empty()) {
int u = q.front();
q.pop();
for(auto x : G[u]) {
-- Deg[x];
if(Deg[x] == 0) q.push(x);
}
}
for(int i = 1 ; i <= n ; ++ i)
if(Deg[i] && vis[i]) return true;
return false;
}
inline void BFS() {
q.push(S);
for(int i = 1 ; i <= n ; ++ i)
dis[i][0] = dis[i][1] = 1e18;
dis[S][0] = 0;
while(! q.empty()) {
int u = q.front();
q.pop();
for(auto x : g[u]) {
if(dis[u][0] + 1 < dis[x][1]) {
q.push(x);
pre[x][1] = u;
dis[x][1] = dis[u][0] + 1;
}
if(dis[u][1] + 1 < dis[x][0]) {
q.push(x);
pre[x][0] = u;
dis[x][0] = dis[u][1] + 1;
}
}
}
return ;
}
signed main() {
ios_base :: sync_with_stdio(NULL);
cin.tie(nullptr);
cout.tie(nullptr);
cin >> n >> m;
for(int i = 1 ; i <= n ; ++ i) {
cin >> deg[i];
for(int j = 1 ; j <= deg[i] ; ++ j) {
cin >> u;
g[i].pb(u);
}
}
cin >> S;
BFS();
for(int i = 1 ; i <= n ; ++ i) {
if(deg[i] == 0) {
if(dis[i][1] != 1e18) {
T = i;
goto place;
}
}
}
dfs(S);
for(int i = 1 ; i <= n ; ++ i)
for(auto j : g[i])
if(vis[i] && vis[j]) {
++ Deg[j];
G[i].pb(j);
}
if(toposort()) cout << "Draw";
else cout << "Lose";
return 0;
place:;
int now = 1;
cout << "Win\n";
while(true) {
ans[++ tot] = T;
if(dis[T][now] == 0) break ;
T = pre[T][now];
now ^= 1;
}
for(int i = tot ; i ; -- i)
cout << ans[i] << ' ';
return 0;
}