POJ 3385 Genealogy

問題

宇宙人は、1人の子に対して親が最大d人までいる。
親子関係がツリーとして与えられるが、一部が欠けていて、親がd人より多くなっている宇宙人がいる。

ツリーに適当に宇宙人を挿入し、1人の子の親がd人以下にしたい。
挿入しなければならない宇宙人の最小の数を求めよ。

制約条件

n≦100000

方針

宇宙人を頂点とするグラフを考える。
宇宙人Aが宇宙人Bの子であるとき、A→Bのように辺を貼る。

このグラフのある頂点において、子ノードがd個以上のm人だったとき、
m→m/d+m%dと言う風に、d人の親をもつ子を置けるだけgreedyに置いてノードを減らすのが最善の置き方。
これは、毎回愚直に計算すると時間がかかるので、あらかじめDPにより求めておく。


あとはこれをグラフの全て頂点に対してやればいい。
が、TLEがやたら厳しい。
O(n)の解法でもTLEする。


グラフでvectorを使わないようにしたら本当にギリギリで(1000ms)通った。
入力をscanf使わず自力でreadするのが吉なのだろうか、

ソースコード

const int MAX=100001;
int n,d;
int dp[MAX];
int G[MAX],es,to[MAX],next[MAX],deg[MAX];

int dfs(int c){
  int m=deg[c], res=dp[m];
  for(int e=G[c];e;e=next[e])res+=dfs(to[e]);
  return res;
}
int main(){
  scanf("%d%d",&n,&d);
  es=1;
  rep(i,n){
    int a; scanf("%d",&a);
    deg[a]++;
    next[es]=G[a];
    to[es]=i+1;
    G[a]=es++;
  }
  for(int i=d+1;i<=n;i++)
    dp[i]=i/d+dp[i/d+i%d];
  
  printf("%d\n",dfs(0));
  return 0;
}