Problem:
Score : 800 points

Problem Statement

There is a directed graph with N vertices and N edges. The vertices are numbered 1, 2, ..., N.

The graph has the following N edges: (p_1, 1), (p_2, 2), ..., (p_N, N), and the graph is weakly connected. Here, an edge from Vertex u to Vertex v is denoted by (u, v), and a weakly connected graph is a graph which would be connected if each edge was bidirectional.

We would like to assign a value to each of the vertices in this graph so that the following conditions are satisfied. Here, a_i is the value assigned to Vertex i.

Each a_i is a non-negative integer.

For each edge (i, j), a_i \neq a_j holds.

For each i and each integer x(0 ≤ x < a_i), there exists a vertex j such that the edge (i, j) exists and x = a_j holds.

Determine whether there exists such an assignment.

Constraints

2 ≤ N ≤ 200 000

1 ≤ p_i ≤ N

p_i \neq i

The graph is weakly connected.

Input

Input is given from Standard Input in the following format:

N
p_1 p_2 ... p_N

Output

If the assignment is possible, print POSSIBLE; otherwise, print IMPOSSIBLE.

Sample Input 1

4
2 3 4 1

Sample Output 1

POSSIBLE

The assignment is possible: {a_i} = {0, 1, 0, 1} or {a_i} = {1, 0, 1, 0}.

Sample Input 2

3
2 3 1

Sample Output 2

IMPOSSIBLE

Sample Input 3

4
2 3 1 1

Sample Output 3

POSSIBLE

The assignment is possible: {a_i} = {2, 0, 1, 0}.

Sample Input 4

6
4 5 6 5 6 4

Sample Output 4

IMPOSSIBLE

Solution in Rust (1.15.1) (judge verdict: Accepted)

use std::io::{stdin, Read};
use std::str::FromStr;
use std::collections::HashSet;
use std::process;

fn dfs(e: &Vec<Vec<usize>>, u: usize, p: usize, dep: &mut Vec<usize>, d: usize) {
    dep[u] = d;
    for i in 0..e[u].len() {
        let to = e[u][i];
        if p == to {
            continue;
        }
        dfs(e, to, u, dep, d + 1);
    }
}
fn get_path(e: &Vec<Vec<usize>>, u: usize, v: usize, n: usize) -> Vec<usize> {
    let mut d = vec![0; n];
    dfs(e, u, n, &mut d, 0);
    let mut path = vec![];
    let mut p = v;
    path.push(p);
    while p != u {
        for i in 0..e[p].len() {
            let to = e[p][i];
            if d[p] > d[to] {
                p = to;
                break;
            }
        }
        path.push(p);
    }
    path
}
fn dfs2(e: &Vec<Vec<usize>>, u: usize, val: &mut Vec<usize>, cycle: &Vec<bool>, n: usize) -> usize {
    let mut v = HashSet::<usize>::new();
    for i in 0..e[u].len() {
        let to = e[u][i];
        if cycle[to] {
            continue;
        }
        v.insert(dfs2(e, to, val, cycle, n));
    }
    let mut h = 0;
    for i in 0..n {
        if !v.contains(&i) {
            val[u] = i;
            h = i;
            break;
        }
    }
    h
}
fn dfs3(
    e: &Vec<Vec<usize>>,
    u: usize,
    val: &mut Vec<usize>,
    cycle: &Vec<bool>,
    n: usize,
    last: usize,
) -> usize {
    let mut v = HashSet::<usize>::new();
    for i in 0..e[u].len() {
        let to = e[u][i];
        if cycle[to] && to != last {
            v.insert(dfs3(e, to, val, cycle, n, last));
        } else {
            v.insert(val[to]);
        }
    }
    //println!("{} visited", u);
    let mut h = 0;
    for i in 0..n {
        if !v.contains(&i) {
            val[u] = i;
            h = i;
            break;
        }
    }
    h
}
fn check(e: &Vec<Vec<usize>>, val: &Vec<usize>, n: usize) -> bool {
    let mut flag = true;
    for i in 0..n {
        let mut v = vec![false; val[i]];
        let mut cnt = 0;
        for j in 0..e[i].len() {
            if val[i] == val[e[i][j]] {
                flag = false;
            }
            if val[i] > val[e[i][j]] && !v[val[e[i][j]]] {
                cnt += 1;
                v[val[e[i][j]]] = true;
            }
        }
        if cnt != val[i] {
            flag = false;
            break;
        }
    }
    flag
}

fn main() {

    let n = read::<usize>();
    let mut uf = UFind::new(n);
    let mut e = vec![vec![]; n];
    let mut d_e = vec![vec![]; n];
    let mut p = read_vec::<usize>(n);
    let mut path = vec![];
    let mut cycle = vec![false; n];
    for i in 0..n {
        p[i] -= 1;
        if uf.same(i, p[i]) {
            path = get_path(&e, i, p[i], n);
            //println!("{:?}", path);
        }
        uf.unite(i, p[i]);
        e[i].push(p[i]);
        e[p[i]].push(i);
        d_e[p[i]].push(i);
    }

    for i in &path {
        cycle[*i] = true;
    }
    let mut val = vec![0; n];
    for i in &path {
        dfs2(&d_e, *i, &mut val, &cycle, n);
    }

    path.reverse();
    let mut v = vec![false; n];
    for i in &d_e[path[0]] {
        if cycle[*i] {
            continue;
        }
        v[val[*i]] = true;
    }
    let mut cnt = 0;
    for i in 0..n {
        if !v[i] {
            val[path[0]] = i;
            dfs3(&d_e, path[1], &mut val, &cycle, n, path[0]);
            //println!("{:?}", val);
            if check(&d_e, &val, n) {
                println!("POSSIBLE");
                process::exit(0);
            }
            cnt += 1;
            if cnt == 2 {
                break;
            }
        }
    }
    println!("IMPOSSIBLE");
}
#[allow(dead_code)]
struct UFind {
    n: usize,
    par: Vec<usize>,
    rank: Vec<usize>,
    count: Vec<usize>,
}
#[allow(dead_code)]
impl UFind {
    pub fn new(n: usize) -> UFind {
        let mut par = vec![0; n];
        for i in 0..n {
            par[i] = i;
        }
        UFind {
            n: n,
            par: par,
            rank: vec![0; n],
            count: vec![1; n],
        }
    }

    pub fn find(&mut self, x: usize) -> usize {
        if self.par[x] == x {
            x
        } else {
            let par = self.par[x];
            let find = self.find(par);
            self.par[x] = find;
            find
        }
    }
    fn same(&mut self, a: usize, b: usize) -> bool {
        self.find(a) == self.find(b)
    }
    fn size(&mut self, a: usize) -> usize {
        let b = self.find(a);
        self.count[b]
    }
    fn unite(&mut self, a: usize, b: usize) {
        let _a = self.find(a);
        let _b = self.find(b);
        if self.rank[_a] > self.rank[_b] {
            self.par[_b] = _a;
            self.count[_a] += self.count[_b];
        } else {
            self.par[_a] = _b;
            self.count[_b] += self.count[_a];
            if self.rank[_a] == self.rank[_b] {
                self.rank[_b] += 1;
            }
        }
    }
}

#[allow(dead_code)]
fn read_vec<T: FromStr>(n: usize) -> Vec<T> {
    let mut v = vec![];
    for i in 0..n {
        let a = read::<T>();
        v.push(a);
    }
    v
}
#[allow(dead_code)]
fn read_str() -> Vec<char> {
    let s: String = read();
    s.chars().collect::<Vec<char>>()
}
#[allow(dead_code)]
fn read<T: FromStr>() -> T {
    let stdin = stdin();
    let stdin = stdin.lock();
    let s = stdin
        .bytes()
        .map(|c| c.unwrap() as char)
        .skip_while(|c| c.is_whitespace())
        .take_while(|c| !c.is_whitespace())
        .collect::<String>();
    s.parse::<T>().unwrap_or_else(
        |_| panic!("Faild to parse {}", s),
    )
}