ARC079D题解

首先根据题目可以得到两个信息:

  • 给定的图是一颗基环外向树。

  • 每个点的权值是其所有出边指向的点 \(v\) 的权值 \(a_{v}\)\(mex\)

其中第二点可以通过简单的反证法得到。

  1. 非环部分

    可以直接dfs,从叶子结点开始往上确定整棵树。

    这样,就已经可以处理非环部分的每一颗子树了,问题就只剩下了环上的点。

  2. 环上部分

    看到大部分人对于环这里都是采用了一些比较高妙的做法。

    但是我比较菜,想不到,于是直接敲了个“暴力“。

    可以发现只要确定了环上的任意一个点,整个环也就被确定了。

    那么不妨随便选一个点 \(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;
}
posted @ 2021-04-30 13:26  krimson  阅读(47)  评论(0编辑  收藏  举报