ARC079D题解
首先根据题目可以得到两个信息:
-
给定的图是一颗基环外向树。
-
每个点的权值是其所有出边指向的点 \(v\) 的权值 \(a_{v}\) 的 \(mex\) 。
其中第二点可以通过简单的反证法得到。
-
非环部分
可以直接
dfs
,从叶子结点开始往上确定整棵树。这样,就已经可以处理非环部分的每一颗子树了,问题就只剩下了环上的点。
-
环上部分
看到大部分人对于环这里都是采用了一些比较高妙的做法。
但是我比较菜,想不到,于是直接敲了个“暴力“。
可以发现只要确定了环上的任意一个点,整个环也就被确定了。
那么不妨随便选一个点 \(u\),枚举\(a_u\),填完整个环之后反过来check一下 \(a_u\) 是不是 \(mex_u\)。
由于 \(a_{u}\leq n\) ,所以这里已经有一个 \(O(n^2)\) 的做法了。
但是前面所得的这个 \(a_{u}\)的上界显然比较粗略,尝试做一点优化。
对于选定的 \(u\),把它除去环上的点的儿子的点权去重之后的个数 \(siz\) 求出来,显然有 \(a_{u}\leq siz+1\)
然后一交上去发现跑得飞快实际上,这种做法的复杂度是正确的。
考虑到一个非环点 \(v\) 的点权是 \(a_v\) ,这意味着它的儿子的权值至少满足分别为 \(1\)~\(a_{v}-1\)。
设 \(f(x)\) 表示一个权值为 \(x\) 的点为根的子树里最少点的数量,那么可以得到:
\[\begin{aligned} f(x)&=1+\sum^{x-1}_{i=0}f(i)\\ f(0)&=1 \end{aligned} \]所以有 \(f(x)=2^x\) 。
所以枚举的次数是 \(O(\log n)\),总时间复杂度为 \(O(n\log n)\)。
Code
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
#define il inline
#define ri register int
#define ll long long
#define ui unsigned int
il ll read(){
bool f=true;ll x=0;
register char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=false;ch=getchar();}
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
if(f) return x;
return ~(--x);
}
il void write(const ll &x){if(x>9) write(x/10);putchar(x%10+'0');}
il void print(const ll &x) {x<0?putchar('-'),write(~(x-1)):write(x);putchar('\n');}
il ll max(const ll &a,const ll &b){return a>b?a:b;}
il ll min(const ll &a,const ll &b){return a<b?a:b;}
const int MAXN=2e5+7;
int mark[MAXN],col[MAXN],n;
int vec[MAXN],top;
int a[MAXN];
vector<int> g[MAXN];
int mex(int u){
top=0;
for(ri i=0;i<g[u].size();++i){
int v=g[u][i];
vec[++top]=a[v];
}
sort(vec+1,vec+top+1);
int ans=0;
for(ri i=1;i<=top;++i){
if(vec[i]==ans) ++ans;
else if(vec[i]>ans) break;
}
return ans;
}
void get_circle(int u){
for(ri i=0;i<g[u].size();++i){
int v=g[u][i];
if(!col[v]){
col[v]=col[u];
get_circle(v);
if(mark[v]) mark[u]=1;
}
else if(col[u]==col[v]){
mark[u]=1;
return;
}
}
}
void dfs(int u){
int now=0;
for(ri i=0;i<g[u].size();++i){
int v=g[u][i];
dfs(v);
}
a[u]=mex(u);
}
int root,tot=1e9;
int vis[MAXN],fa[MAXN];
int main(){
n=read();
for(ri i=1;i<=n;++i){
int u=read();
g[u].push_back(i);
}
for(ri i=1;i<=n;++i){
if(!col[i]) col[i]=i,get_circle(i);
if(mark[i]) break;
}
for(ri i=1;i<=n;++i){
if(mark[i]){
vector<int> revec;
int cnt=0;
for(ri j=0;j<g[i].size();++j){
int u=g[i][j];
if(mark[u]){
fa[u]=i;
continue;
}
dfs(u);
revec.push_back(a[u]);
}
sort(revec.begin(),revec.end());
revec.erase(unique(revec.begin(),revec.end()),revec.end());
cnt=revec.size()+1;
if(cnt<tot) root=i,tot=cnt;
}
}
vector<int> vec;
for(ri i=0;i<=tot+1;++i){
a[root]=i;
for(ri u=fa[root];u!=root;u=fa[u])
a[u]=mex(u);
if(mex(root)==i) return !puts("POSSIBLE");
}
puts("IMPOSSIBLE");
return 0;
}