1. 程式人生 > >DMOJ IOI '17 P3 - Toy Train【拓撲排序】

DMOJ IOI '17 P3 - Toy Train【拓撲排序】

ack () sin .net 參考 emp 全部 article namespace

傳送:https://dmoj.ca/problem/ioi17p3
參考:https://blog.csdn.net/qq_27327327/article/details/80711824
妙啊……首先題意就是走到一個包含充電點的環裏就能贏
因為出度至少是1,所以如果所有點都能到充電點那麽全部是先手必勝;否則,不能到充點電的點以及一定能到這些點的點就一定是先手必敗(能到的不是先手必勝,因為可能到了之後再出去進入別的環)
能到充點電的點是A支配並且能到至少一個充點電的點或B支配只能到充電點的點,每次求出這個集合,判斷如果是全集就退出,否則用補集找到先手必敗點刪去,然後再對剩下不確定的點做上述操作即可

#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
using namespace std;
const int N=5005;
int n,m;
vector<int>e[N],e2[N];
vector<int>wk(int fl,vector<int>a,vector<int>r,vector<int>b)
{
    vector<int>ans(n),d(n);
    queue<int>q;
    for(int i=0;i<n;i++)
        if(r[i]&&b[i])
            q.push(i),ans[i]=1;
    for(int i=0;i<n;i++)
        for(int j=0;j<e[i].size();j++)
            if(b[e[i][j]])
            {
                if(a[i]^fl)
                    d[i]++;
                else
                    d[i]=1;
            }
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=0;i<e2[u].size();i++)
            if(!ans[e2[u][i]]&&b[e2[u][i]])
                if(!(--d[e2[u][i]]))
                {
                    ans[e2[u][i]]=1;
                    q.push(e2[u][i]);
                }
    }
    return ans;
}
vector<int> who_wins(vector<int>a,vector<int>r,vector<int>u,vector<int>v)
{
    n=a.size(),m=u.size();
    for(int i=0;i<m;i++)
        e[u[i]].push_back(v[i]),e2[v[i]].push_back(u[i]);
    vector<int>ans(n);
    for(int i=0;i<n;i++)
        ans[i]=1;
    while(1)
    {
        int fl=1;
        vector<int>b1=wk(1,a,r,ans);
        for(int i=0;i<n;i++)
            if(ans[i]&&!b1[i])
                fl=0;
        if(fl)
            return ans;
        for(int i=0;i<n;i++)
            b1[i]^=1;
        vector<int>b2=wk(0,a,b1,ans);
        for(int i=0;i<n;i++)
            if(b2[i])
                ans[i]=0;
    }
    return ans;
}

DMOJ IOI '17 P3 - Toy Train【拓撲排序】