ARC079F - Namori Grundy
Solution
首先这是一个 N N N个点 N N N条边的有向图,所以它的基图是一棵基环树,其次这个图的所有点入度为 1 1 1,因此这是一棵基环外向树。
然后对于 a i a_i ai,假设我们求出 S = { a j ∣ ( i , j ) ∈ E } S=\{a_j|(i,j)\in E\} S={aj∣(i,j)∈E},即 i i i的所有出边的 a a a的集合,那么显然 a i = m e x S a_i=mex\;S ai=mexS, a i a_i ai的值是可以通过其出边唯一确定的。
我们先考虑一棵树的情况,我们发现叶子结点必然为 0 0 0,因此每一个结点 i i i的 a i a_i ai都可以从下到上通过其儿子结点递推得到。
现在考虑基环树,对于环上一点,我们可以通过它的子树唯一确定去掉环时它的 a a a,然后可以发现当且仅当环长为奇数且环上结点的 a a a都相等时无解。
然后直接找到环,递推求出每一个 a a a,判一判就行了。
时间复杂度 O ( n ) O(n) O(n)。
Proof
现在我们可以把子树点都扔掉,只考虑一个
k
k
k元环。
我们设环上点依次为
0
,
1
,
2...
k
−
1
0,1,2...k-1
0,1,2...k−1,
(
i
+
1
,
i
)
∈
E
(i+1,i)\in E
(i+1,i)∈E,(之后的下标都在模
k
k
k意义下)。
- 若 a i + 1 a_{i+1} ai+1要加 1 1 1,当且仅当 a i + 1 = a i a_{i+1}=a_i ai+1=ai。
- 因为环上点出度为 1 1 1,一个点最多加 1 1 1。
首先我们不会让所有点都加 1 1 1,因为这和所有点不变一样,所以若存在方案,一定有一种有至少一个点 p p p不动的方案。
如果我们知道 p p p,那么我们可以从 p p p开始判断 a p a_p ap是否等于 a p + 1 a_{p+1} ap+1,若相等则 a p + 1 + 1 a_{p+1}+1 ap+1+1。
我们令一段连续的 + 1 +1 +1的下标区间为 [ l + 1 , r ] [l+1,r] [l+1,r],显然有 a l = a l + 1 , a l + 1 + 1 = a l + 2 , a l + 2 + 1 = a l + 3 . . . a_{l}=a_{l+1},a_{l+1}+1=a_{l+2},a_{l+2}+1=a_{l+3}... al=al+1,al+1+1=al+2,al+2+1=al+3...,因此这段 a a a一定是 a l , a l , a l + 1 , a l + 2... a l + ( r − l − 1 ) a_l,a_l,a_l+1,a_l+2...a_l+(r-l-1) al,al,al+1,al+2...al+(r−l−1),点 l l l不动且为其中的最小值。
所以我们有结论:若能找到一个 i i i,使得 a i ≠ a i − 1 a_i\not = a_{i-1} ai=ai−1且 ∀ j a i ≤ a j \forall_j a_i\leq a_j ∀jai≤aj, i i i可以作为 p p p,方案存在。
那么最后还剩下找不到 i i i的环,其上的点必然是所有 a a a相等的,这个就很容易考虑了,因为所有点都等价,所以可以任取一个作为 p p p,最后的序列一定长成 a p , a p + 1 , a p , a p + 1 , a p , a p + 1... a_p,a_p+1,a_p,a_p+1,a_p,a_{p}+1... ap,ap+1,ap,ap+1,ap,ap+1...的形式。
这就相当于一个二分图染色,只有当 k k k为奇数时,存在相邻两个 a a a相等,不合法;当 a a a为偶数时存在合法方案。
综上,当且当且仅当环长为奇数且环上结点的 a a a都相等时无解。
Code
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <ctime>
#include <cassert>
#include <string.h>
//#include <unordered_set>
//#include <unordered_map>
//#include <bits/stdc++.h>
#define MP(A,B) make_pair(A,B)
#define PB(A) push_back(A)
#define SIZE(A) ((int)A.size())
#define LEN(A) ((int)A.length())
#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define fi first
#define se second
using namespace std;
template<typename T>inline bool upmin(T &x,T y) { return y<x?x=y,1:0; }
template<typename T>inline bool upmax(T &x,T y) { return x<y?x=y,1:0; }
typedef long long ll;
typedef unsigned long long ull;
typedef long double lod;
typedef pair<int,int> PR;
typedef vector<int> VI;
const lod eps=1e-11;
const lod pi=acos(-1);
const int oo=1<<30;
const ll loo=1ll<<62;
const int mods=998244353;
const int MAXN=600005;
const int INF=0x3f3f3f3f;//1061109567
/*--------------------------------------------------------------------*/
inline int read()
{
int f=1,x=0; char c=getchar();
while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }
while (c>='0'&&c<='9') { x=(x<<3)+(x<<1)+(c^48); c=getchar(); }
return x*f;
}
vector<int> e[MAXN];
int fa[MAXN],vis[MAXN],instk[MAXN],flag[MAXN],tag[MAXN],f[MAXN],n;
void dfs(int x)
{
vis[x]=1,instk[x]=1;
for (auto v:e[x])
{
if (instk[v])
{
for (int p=x;p!=v;p=fa[p]) flag[p]=1;
flag[v]=1;
}
else fa[v]=x,dfs(v);
}
instk[x]=0;
}
void tree_dp(int x,int father)
{
for (auto v:e[x]) if (v!=father&&!flag[v]) tree_dp(v,x);
for (auto v:e[x]) if (v!=father&&!flag[v]) tag[f[v]]=x;
for (int i=0;i<n;i++)
if (tag[i]!=x) { f[x]=i; return; }
}
signed main()
{
n=read();
for (int i=1,x;i<=n;i++) x=read(),e[x].PB(i);
for (int i=1;i<=n;i++) if (!vis[i]) dfs(i);
int p=0,num=0;
for (int i=1;i<=n;i++)
if (flag[i]) tree_dp(i,0),p=f[i],num++;
if (!(num&1)) { puts("POSSIBLE"); return 0; }
for (int i=1;i<=n;i++)
if (flag[i]&&p!=f[i]) { puts("POSSIBLE"); return 0; }
puts("IMPOSSIBLE");
return 0;
}