TopCoder SRM 531 Div1 Medium MonsterFarm
問題
1〜n番までのn種類のモンスターがいる。
最初は1番のモンスターが1匹いる。
一日たつと、i番目のモンスターは、それぞれ与えられたモンスター(一匹以上)へと変化する。
最終的にモンスターは何匹になるか、
モンスターが無限に増える場合は-1を、そうでないならその数をmod 10^9 + 7で求めよ。
制約条件
n≦50
方針
モンスターの変化の関係をグラフにする。
すなわち、i番目のモンスターからj番目のモンスターが1匹生まれるとき、
iからjへ重さ1の辺を張る。
モンスターが無限に増えるのは、このグラフを強連結成分分解したときに、
(最初のモンスターから到達可能な)一つの連結成分のなかに、
出次数2以上の頂点があることと同値。
ただしセルフループがある場合がちょっとコーナーケースになっているので別途処理する。
上の条件を満たす場合-1を出力し、そうでないときは100ターンしないうちにモンスターの数は変化しなくなるので、
100ターンシミュレーションしてその数を出力すればいい。
ソースコード
const int mod=(int)1e9+7; int n, e[50][50]; int out[50], nk, cmp[50]; bool can[50][50]; struct MonsterFarm{ int numMonsters(vector<string> transforms){ n=transforms.size(); for(int i=0;i<n;i++){ stringstream ss(transforms[i]); int t; while(ss>>t)e[i][--t]++, out[i]++; } for(int i=0;i<n;i++)for(int j=0;j<n;j++)can[i][j]=e[i][j]; for(int k=0;k<n;k++)for(int i=0;i<n;i++)for(int j=0;j<n;j++)can[i][j]|=can[i][k]&&can[k][j]; for(int i=0;i<n;i++)if(can[0][i]&&e[i][i]>=1&&e[i][i]+out[i]>2)return -1; nk=0; for(int i=0;i<n;i++)cmp[i]=-1; for(int i=0;i<n;i++)if(cmp[i]<0){ for(int j=i+1;j<n;j++)if(can[i][j]&&can[j][i])cmp[j]=nk; cmp[i]=nk++; } for(int i=0;i<nk;i++){ int cnt=0; for(int j=0;j<n;j++)if(cmp[j]==i&&can[0][j])cnt++; for(int j=0;j<n;j++)if(cmp[j]==i&&cnt>1&&out[j]>1)return -1; } vector<int> cnt(n); cnt[0]=1; for(int i=0;i<100;i++){ vector<int> nxt(n); for(int j=0;j<n;j++)for(int k=0;k<n;k++)(nxt[k]+=(long long)cnt[j]*e[j][k]%mod)%=mod; cnt=nxt; } int ans=0; for(int i=0;i<n;i++)(ans+=cnt[i])%=mod; return ans; } };