ARC079F - Namori Grundy(构造,基环树)

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...k1 ( i + 1 , i ) ∈ E (i+1,i)\in E (i+1,i)E,(之后的下标都在模 k k k意义下)。

  1. a i + 1 a_{i+1} ai+1要加 1 1 1,当且仅当 a i + 1 = a i a_{i+1}=a_i ai+1=ai
  2. 因为环上点出度为 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+(rl1),点 l l l不动且为其中的最小值。

所以我们有结论:若能找到一个 i i i,使得 a i ≠ a i − 1 a_i\not = a_{i-1} ai=ai1 ∀ j a i ≤ a j \forall_j a_i\leq a_j jaiaj 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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值