Lyft Level 5 Challenge 2018 C. Permutation Game

地址:http://codeforces.com/contest/1033/problem/C

必胜点和必败点的概念:
P点:必败点,换而言之,就是谁处于此位置,则在双方操作正确的情况下必败。
N点:必胜点,处于此情况下,双方操作均正确的情况下必胜。
必胜点和必败点的性质:
1、所有终结点是 必败点 P 。(我们以此为基本前提进行推理,换句话说,我们以此为假设)
2、从任何必胜点N 操作,至少有一种方式可以进入必败点 P。
3、无论如何操作,必败点P 都只能进入 必胜点 N。

这道题,两种做法:
第一种做法:拓扑+博弈
由于是一个排列,那么可以枚举每个数,判断这个位置的a是否大于它,如果可以连边。这样的复杂度是nlogn的。。。
然后对于一些无路可走的点,这是必败态,根据必胜和必败的定义:必胜态的后继状态中存在至少一个必败态,必败态的后继状态全是必胜态。
然后建反图,在DAG上拓扑。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int inf = 0x3f3f3f3f;
const double PI = acos(-1);
const double eps = 1e-8;
#define pb push_back
#define mp make_pair
#define fi first
#define se second
const int N = 1e5 + 5;

int a[N];
vector<int>ve[N];
int deg[N];
int ans[N];

int main()
{
    int n;
    scanf("%d",&n);
    for(int i = 1;i <=  n;++i){
        scanf("%d",&a[i]);
    }
    memset(deg,0,sizeof(deg));
    memset(ans,-1,sizeof(ans));
    for(int i = 1;i <= n;++i){
        for(int j = i;j <= n;j += a[i]){
            if(a[j] > a[i]) ve[j].pb(i),deg[i]++;
        }
        for(int j = i;j >= 1;j -= a[i]){
            if(a[j] > a[i]) ve[j].pb(i),deg[i]++;
        }
    }
    queue<int>que;
    for(int i = 1;i <= n;++i){
        if(!deg[i]) que.push(i),ans[i] = 0;
    }
    while(!que.empty())
    {
        int u = que.front();
        que.pop();
        int len = ve[u].size();
        for(int i = 0;i < len;++i){
            int v = ve[u][i];
            if(ans[v] == -1){
                if(ans[u] == 0) ans[v] = 1;
                else ans[v] = 0;
            }else{
                if(ans[u] == 0) ans[v] = 1;
            }
            deg[v]--;
            if(!deg[v]) que.push(v);
        }
    }
    for(int i = 1;i <= n;++i){
        if(ans[i]) printf("A");
        else printf("B");
    }
    printf("\n");
    return 0;
}

第二种做法:
逆向处理n…1;算ans[i]的时候,i + 1 …n的ans值已经算出,与i相关的值必须是比i大的,那么找i 的后继状态,存在一个必败态,则ans[i]为必胜态,如果找到的都是必胜态的,那么ans[i]为必败态

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int inf = 0x3f3f3f3f;
const double PI = acos(-1);
const double eps = 1e-8;
#define pb push_back
#define mp make_pair
#define fi first
#define se second
const int N = 1e5 + 5;

int a[N];
int b[N];
int ans[N];

int main()
{
    int n;
    scanf("%d",&n);
    for(int i = 1;i <= n;++i){
        scanf("%d",&a[i]);
        b[a[i]] = i;
        //初始化为必败态
        ans[i] = 0;
    }
    //倒着遍历,是每个i找到的j的ans值是已经求出来了
    for(int i = n;i >= 1;--i){
        //找到i的下标
        int x = b[i];
        //x % i是为了找到第一个满足(j - x) % i == 0的下标
        for(int j = x % i;j <= n;j += i){
            //只要后继状态存在一个必败态,那么当前必是必胜态
            if(a[j] > a[x]) ans[x] |= (!ans[j]);
        }
    }
    for(int i = 1;i <= n;++i){
        if(ans[i])
            printf("A");
        else
            printf("B");
    }
    printf("\n");
    return 0;
}


猜你喜欢

转载自blog.csdn.net/qq_36386435/article/details/83031997
今日推荐