[arc079F]Namori Grundy

Description

​ 一个有向弱联通环套树。  一个点的sg值等于出边连向点的\(sg\)值的\(mex\)。试问是否有办法给每个点分配\(sg\)值。

​ 注:基环内向树

o_20180722102406647.png

​ 基环外向树

o_20180722102721493.png

居然有水印!!!

Hint

\(2\leq N \leq 200000 \\ 1\leq p_i \leq N\quad p_i\neq i\)

Solution

​ 首先,一棵树一定有解,且每个树上的节点的权值唯一确定。然后我们看题目,基环分两类:基环外向树,基环内向树。然而由于所有的点入度大于等于1,所以并没有内向树。

​ 那么现在只剩下基环外向树,我们先把环断掉一条\((u,v)\ : \ u \to v\),原图变成一颗外向树,计算树上节点的\(sg\)值,对于\(u\)来说,设当前外向树上\(u\)\(sg\)值为\(tmp_1\)\(u\)的儿子中没有出现的第二大的值为\(tmp_2\)(第一大的是\(tmp_1\)),那么\(sg_u \in \{tmp_1, tmp_2 \}\)

1、\(sg_u \ = \ tmp_1\):考虑连上边\((u,v)\)后的影响,那么只要满足$sg_v  \not =  tmp_1 $即可。

2、\(sg_u \ = \ tmp_2\):这时\(,sg_v \ = \ tmp_1 , sg_u \ = \ tmp_2\),那么只需从u开始往父亲跳,判断是否合法即可。

Code

/**************************************************************
    Problem: 3177
    User: ez_hjw
    Language: C++
    Result: Accepted
    Time:111 ms
    Memory:16148 kb
****************************************************************/
 
 
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn = 200000 + 10;
 
int n, p[maxn], tim = 0, Sx, Tx, col[maxn], sg[maxn];
int cnt, h[maxn];
struct enode{
  int v, n, op;
  enode() {}
  enode(int _v, int _n):v(_v), n(_n) { op = 0; }
}e[maxn << 1];
 
inline void addedge(int u, int v) {
  cnt ++; e[cnt] = enode(v,h[u]); h[u] = cnt;
}
 
int vis[maxn << 1];
inline int get_sg(int u) {
  for(int i = h[u];~ i;i = e[i].n) {
    int v = e[i].v;
    if(e[i].op) continue;
    vis[sg[v]] = 1;
  }
  int tmp = -1;
  for(int i = 0;;i ++) if(!vis[i]) {
    tmp = i;
    break;
  }
  for(int i = h[u];~ i;i = e[i].n) {
    int v = e[i].v;
    if(e[i].op) continue;
    vis[sg[v]] = 0;
  }
  return tmp;
}
 
void dfs(int u) {
  col[u] = tim;
  for(int i = h[u];~ i;i = e[i].n) {
    int v = e[i].v;
    if(e[i].op) continue;
    if(col[v]) {
      if(col[v] == tim) {
        Sx = u; Tx = v;
        e[i].op = 1;
      }
    }
    else dfs(v);
  }
  sg[u] = get_sg(u);
}
 
int main() {
  scanf("%d", &n); cnt = 0;
  memset(h,-1,sizeof(h));
  for(int i = 1;i <= n;i ++) {
    scanf("%d", &p[i]);
    addedge(p[i],i);
  }
  for(int i = 1;i <= n;i ++) {
    if(!col[i]) {
      tim ++;
      dfs(i);
    }
  }
  int tmp1 = get_sg(Sx);
  vis[tmp1] = 1;
  int tmp2 = get_sg(Sx);
  vis[tmp1] = 0;
  if(sg[Tx] != tmp1) {
    puts("POSSIBLE");
    return 0;
  }
  sg[Sx] = tmp2;
  int u = Sx;
  while(u != Tx) {
    u = p[u];
    sg[u] = get_sg(u);
  }
  sg[Tx] = get_sg(Tx);
  if(sg[Tx] != tmp1) puts("IMPOSSIBLE");
  else puts("POSSIBLE");
  return 0;
}

转载于:https://www.cnblogs.com/ezhjw/p/9518523.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值