TopCoder TCO2015R1A Div1 Medium 500 Autogame
問題文概要
Functional Graph, すなわち, 有向グラフであって, 各頂点の出次数がちょうど $1$ であるようなものが与えられる.
自然数 $k$ を指定した時, 各頂点 $v$ に対し, $v$ を始点とする長さ $k$ の walk の終点 $v^k$ が一意に定まる. これを $k$ ターン後の頂点と呼ぶ.
頂点の部分集合 $\\{v_i\\}$ で, $0 \le j \le k$ なる任意の $j$ で $v_i^j$ が distinct なものの個数を求めよ.
制約
$1 \le n \le 50,\ 1 \le k \le 10^9$.
解法
一回同じ頂点になったらその後もずっと同じ頂点だから, $k$ ターン後の頂点 $v_i^k$ が distinct であればよい.
$n$ ターン以内にループに入るから, $\min\\{n, k\\}$ ターンのシミュレートを行うと, $k$ ターン後に同じ頂点になるような頂点達がわかる.
これら, $k$ ターン後に同じ頂点になるやつらは, そのうち一つ選ぶか, 一つも選ばないかの $k+1$ 通りの選び方がある. 同じにならないやつらとは独立なので, それらを掛けあわせればいい.
本番中は $k$ ターン後の位置の一致が推移的なことに気づかず, $k$ ターン後の位置の一致でグラフを作り, 各連結成分で探索をしてしまった. 当然, 連結成分は完全グラフになるので探索は一瞬で終了し, AC はする.
ソースコード
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
constexpr ll mod = 1000 * 1000 * 1000 + 7;
struct UnionFind{ //{{{
vector<int> par;
int n, cnt;
UnionFind(const int &x=0){init(x);}
void init(const int &x){par.assign(cnt = n = x, -1);}
inline int find(const int &x){ return par[x]<0 ? x : par[x] = find(par[x]); }
inline bool same(const int &x, const int &y){ return find(x) == find(y); }
inline bool unite(int x, int y){
if((x = find(x)) == (y = find(y))) return false;
--cnt;
if(par[x] > par[y]) swap(x, y);
par[x] += par[y];
par[y] = x;
return true;
}
inline int count() const { return cnt; }
inline int count(int x){ return -par[find(x)]; }
};
//}}}
ll dfs(ll mask, int u, ll ng, const vector<ll> &g){
for(; u >= 0; --u) if(mask>>u&1) break;
if(u == -1) return 1;
if(ng>>u&1) return dfs(mask, u-1, ng, g);
ll res = 0;
res += dfs(mask, u-1, ng, g);
res += dfs(mask, u-1, ng | g[u], g);
if(res >= mod) res -= mod;
return res;
}
int Autogame::wayscnt( vector <int> a, int K ){
for(aur u : a) --u;
const int n = sz(a);
vector<ll> g(n);
rep(u, n) rep(v, n){
int uu = u, vv = v;
for(int i = 0; i < min(K, n * n); ++i){
uu = a[uu], vv = a[vv];
if(uu == vv){
g[u] |= 1LL<<v;
g[v] |= 1LL<<u;
}
}
}
UnionFind uf(n);
rep(u, n) rep(v, n) if(g[u]>>v&1) uf.unite(u, v);
ll res = 1;
rep(u, n) if(uf.find(u) == u){
ll mask = 0;
rep(v, n) if(uf.find(v) == u) mask |= 1LL<<v;
res = (res * dfs(mask, n, 0, g)) % mod;
}
return res;
}