Lyft Level 5 Challenge 2018 C. Permutation Game
阿新 • • 發佈:2018-11-09
地址: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; }