Loj #3118. 「IOI2017」玩具火车

Description

传送门


Solution

非常妙的一道题。

首先题目中的判定条件“走到拥有关键点的环”是一个不好判定的条件,那么考虑将问题转化变为好判定的。

将原来的操作“保留一条边其它边全部删除”变为“选择一条边走出去”,同时,将获胜条件从“走到拥有关键点的环”变为“可以无限次经过关键点”。

如果能无限次走到环上,那么就相当于之前的走到有关键点的环上,除非后手选择一条边离开环,火车将一直在环上绕圈不停的走关键点,但是如果后手选择了一条离开环的边,那他在第一次经过那个点的时候就不应该选择走上环的边,但是他走上环是因为在那个点走上环最优,所以会产生矛盾,那么转化后的问题和转化前的问题就是等价的了。

考虑“先手可以无限次走关键点”如何判定,如果先手能走无限次关键点,那么对于所有可能走到的点,先手都能迫使后手走到关键点。

即我们求出图上先手能迫使后手走上关键点的点,如果这些点是全集,则后手在这些点必须无限次的走上关键点。

如果这些点不是关键点,那么这些点的补集,一定是后手获胜,以为后手总可以不走到关键点。

同时,先手能迫使后手走上关键点的点可能不能无限次的走上,当且仅当后手在这些点可以进入之前求过的后手必胜的点集。

那么我们发现先手可能获胜的点集又缩小了,继续在这个缩小后的的点集上搜出先手可能获胜的点集,直到满足该点集就是全集的条件,那么剩下的就是先手必胜的点集。

由于每次迭代总会使先手可能获胜的点集变小,所以最多迭代(O(n))次,每次时间复杂度(O(n + m)),总体最坏是(O(n ^ 2 + nm)),可以通过本题。


Code

#include "train.h"
#include <bits/stdc++.h>

using namespace std;

const int N = 5000;

int vis[N + 50], res;

vector<int> edge1[N + 50], edge2[N + 50];

vector<int> Get(vector<int> a, vector<int> res, vector<int> r, int typ)
{
    int n = a.size();
    memset(vis, 0, sizeof(vis));
    vector<int> ans(N + 50), deg(N + 50);
    queue<int> q;
    for (int i = 0; i < n; i++) if (r[i] && res[i]) q.push(i), ans[i] = 1;
    for (int i = 0; i < n; i++) for (int j = 0; j < edge1[i].size(); j++) if (res[edge1[i][j]]) if (typ ^ a[i]) deg[i]++; else deg[i] = 1;
    while (!q.empty())
    {
        int u = q.front(); q.pop();
        for (vector<int>::iterator it = edge2[u].begin(); it != edge2[u].end(); it++)
        {   
            int v = *it;
            if (!ans[v] && res[v])
            {
                deg[v]--;
                if (!deg[v]) ans[v] = 1, q.push(v);
            }
        }
    }
    return ans;
}

vector<int> who_wins(vector<int>a, vector<int>r, vector<int>u, vector<int>v)
{
    int n = a.size(), m = u.size();
    vector<int> res(N + 50);
    for (int i = 0; i < n; i++) res[i] = 1;
    for (int i = 0; i < m; i++) edge1[u[i]].push_back(v[i]), edge2[v[i]].push_back(u[i]);
    while (1)
    {
        int flag = 1;
        vector<int> fa = Get(a, res, r, 1);
        for (int i = 0; i < n; i++)
            if (!fa[i] && res[i])
            {
                flag = 0;
                break;
            }
        if (flag) return res;
        for (int i = 0; i < n; i++) fa[i] ^= 1;
        vector<int> fb = Get(a, res, fa, 0);
        for (int i = 0; i < n; i++) if (fb[i]) res[i] = 0;
    }
    return res;
}
原文地址:https://www.cnblogs.com/Tian-Xing-Sakura/p/13912216.html