地址: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;
}