Problem p02728 (C++) — buggy submission, verdict: Time Limit Exceeded


#include <stdint.h>

using u64 = uint64_t;
using u32 = uint32_t;

// https://atcoder.jp/contests/abc160/submissions/11270339
// イニシャルKさんのコードにあった謎のクラスModuloを移植
class Modulo {
public:
  Modulo(u32 n) {
    facs = new u32[n + 1];
    facs[0] = 1;
    for (u32 i = 1; i <= n; i++) {
      facs[i] = Mul(facs[i - 1], i);
    }
  }
  u32 Mul(u32 a, u32 b) { return (u32)(((u64)a) * b % DivineDivisor); }
  u32 Fac(u32 n) { return facs[n]; }
  u32 Pow(u32 a, u32 m) {
    switch (m) {
    case 0:
      return 1;
    case 1:
      return a;
    default:
      u32 p1 = Pow(a, m / 2);
      u32 p2 = Mul(p1, p1);
      return ((m % 2 == 0) ? p2 : Mul(p2, a));
    }
  }
  u32 Div(u32 a, u32 b) { return Mul(a, Pow(b, DivineDivisor - 2)); }
  u32 Ncr(u32 n, u32 r) {
    if (n < r) {
      return 0;
    }
    if (n == r) {
      return 1;
    }
    auto res = facs[n];
    res = Div(res, facs[r]);
    res = Div(res, facs[n - r]);
    return res;
  }

private:
  constexpr static u32 DivineDivisor = 1000000007;
  u32 *facs;
};

#include "bits/stdc++.h"

using namespace std;

// #define DEBUG

using u64 = uint64_t;
using u32 = uint32_t;
using i64 = int64_t;

struct Link;

// リンクのスコア（計算済パラメータ）
// - 複数のリンクが一つのスコアを参照できる
struct LinkScore {
  LinkScore(u32 nodeCount = 0, u32 patternCount = 0)
      : NodeCount(nodeCount), PatternCount(patternCount) {
    Id = nextId++;
  }
  u32 NodeCount;
  u32 PatternCount;
  u32 Id;

#ifdef DEBUG
  void Print() {
    cout << "[Score n" << NodeCount << " p" << PatternCount << "]";
  }
#endif

private:
  static u32 nextId;
};
u32 LinkScore::nextId = 1;

// ノード: 木のそれぞれの頂点を表す
struct Node {

  int IncompleteLinkCount = 0;

  void Connect(Node *To);

  void AddScore(LinkScore *score) {
    auto it = scores.find(score);
    if (it != scores.end()) {
      it->second++;
    } else {
      scores[score] = 1;
    }
  }

  unordered_map<LinkScore *, u32> GetScores() const { return scores; }

  const list<Link *> *GetCLinks() const { return &links; }

  list<Link *> *GetLinks() { return &links; }

  const list<Link *> *GetCReceiveLinks() const { return &receiveLinks; }

  list<Link *> *GetReceiveLinks() { return &receiveLinks; }

  // 末端か
  bool IsTip() const { return links.size() < 2; }

#ifdef DEBUG
  int Id;
  static int NextId;

  Node() { Id = NextId++; }

  void Print();
#endif

private:
  list<Link *> links, receiveLinks;

  // <LinkScore*, nCount>
  unordered_map<LinkScore *, u32> scores;
};

// リンク: ノード間の繋がりを表す。指向性
struct Link {
  using KeyType = string;

  Link(Node *From, Node *To) : From(From), To(To) {}

  Node *From, *To;
  LinkScore *Score = nullptr;
  Link *Reverse = nullptr;

  Node *GetOtherSideOf(const Node *node) const {
    return node == From ? To : From;
  }

  bool Contains(const Node *node) const { return node == From || node == To; }

  // ロングキーを生成
  KeyType GetLongKey(const Node *startNode) const {
    string result = "";

    // 接続先ノードを始点とするリンクのうち、このリンクの逆向きを
    // 除く全てのリンクのショートキーにより決定する

    // Radial (放射状) テストデータにおいて、
    // ↓に & を一文字書き忘れていたせいでものすごく重くなっていた。。。
    // こういうことが起こらないように class (struct) 側でしたいのだが…
    // ポインタで返すしかないのか？

    // sort(To->Scores.begin(), To->Scores.end());

    const Node *endNode = GetOtherSideOf(startNode);

    auto ignoreScore = this->Reverse->Score;

    for (auto scoreAndCount : endNode->GetScores()) {
      auto count = scoreAndCount.second;
      if (ignoreScore == scoreAndCount.first) {
        count--;
      }
      if (count > 0) {
        result +=
            to_string(scoreAndCount.first->Id) + 'x' + to_string(count) + '+';
      }
    }

    if (result == "") {
      // 末端
      result = "T";
    }

    return result;
  }

#ifdef DEBUG
  void Print() const;
#endif

private:
};

#ifdef DEBUG
int Node::NextId = 0;

// ノード情報を出力
void Node::Print() {
  cout << "Node#" << Id << ":\\n";
  for (auto link : links) {
    cout << " ";
    link->Print();
    cout << '\\n';
  }
  // cout << '\\n';
};

// リンク情報を出力
void Link::Print() const {
  cout << "[Link ";
  cout << From->Id << "->" << To->Id << ' ';
  cout << "key = " << GetLongKey(From);
  if (Score != nullptr) {
    cout << ", ";
    Score->Print();
  }
  cout << "]";
};
#endif

#ifdef DEBUG
// 全ノードを出力
void PrintAllNodes(Node *nodes, int count) {
  for (int i = 0; i < count; i++) {
    nodes[i].Print();
  }
}
#endif

// 相互リンクを張る
void Node::Connect(Node *To) {
  auto link = new Link(this, To);

  links.push_back(link);
  To->receiveLinks.push_back(link);
  IncompleteLinkCount++;

  auto rlink = new Link(To, this);

  To->links.push_back(rlink);
  receiveLinks.push_back(rlink);
  To->IncompleteLinkCount++;

  link->Reverse = rlink;
  rlink->Reverse = link;
}

void Start() {
  int nodeCount;
  cin >> nodeCount;

  Node *nodes = new Node[nodeCount + 1];

  for (int i = 1; i <= nodeCount - 1; i++) {
    int a, b;
    cin >> a >> b;
    nodes[a].Connect(&nodes[b]);
  }

#if defined(DEBUG) && 0
  cout << "*Initial State*\\n";
  PrintAllNodes(nodes, nodeCount + 1);
  cout << '\\n';
#endif

  // Linkの Key/LinkScore 対応表
  unordered_map<Link::KeyType, unique_ptr<LinkScore>> linkKey2Score;

#ifdef DEBUG
  // 処理必要なリンク残り
  int remaining = (nodeCount - 1) * 2;
#endif

  // 計算予定のリンク
  queue<Link *> linkQueue;

  // Modulo
  Modulo modulo(200000);

  // リンクを結合する:
  // 複数のリンクのノード数、パターン数を合わせた
  // ノード数（ただ足せばいい）、パターン数（これが大変）を計算する
  auto combineLink = [&](Node *node, const Link *ignoreLink = nullptr) {
    // 一つのノードから見たパターン数の計算は、以下の2つの問題に分けられる。
    // A.どのリンク先（の集合）から選ぶか
    // B.それぞれのリンク先（の集合）について、どれから選ぶか
    // AとBそれぞれのパターン数を乗算すればいいはず。

    // A.
    // リンク先集合のノード数がたとえば2, 2, 1 なら
    // 積み木が2, 2,
    // 1個積みあがって3つの塔になっていて、毎回一つ上の積み木を取り出す。
    // どの塔から順に取り出すか、という選び方のパターンを計算すればいい。
    //
    // 左から順にA, B, Cの塔とすると
    // たとえばBAACBのような取り出し順がある。
    //
    // *計算法
    // 左から順に処理する。上の例で行く
    // まずAが2つ並んでいるとする。AA
    // これにBを2つ、順に差し込んでいく。AABB, BAABなど
    // 既に並んでいる数をp, 差し込む数をqとすると
    // 差し込める箇所はp + 1個。p + 1個から q 個選びだす組み合わせの公式でいける
    // そうして次はCを差し込み、また公式を使う
    // これを、塔がなくなるまで繰り返す
    // ...
    // と思ったが上の方法ではだめだった。やり直す。
    // A B CCC この例を考える。
    // B を A に入れる方法は |A| の2パターン ...[1]
    // CをABに順に入れていく。1つ目を入れる位置は|A|B|の3か所
    // しかし2個目はCが追加されているので、|A|B|C|の4か所になる
    // つまり3個追加するには、3*4*5。Cは区別しないので、Cの順番のパターンである3*2*1で割る
    // よって答えは10。 [1]をかけると20。
    // これはA B CCC の並び順のパターン数に等しい。よってこちらの計算法を使う。
    // ...
    // 既に並んでいる数を p 個、これから差し込む数を q 個とすると
    // p + q 個 から q 個選び出す組み合わせの公式でいける。きっといける

    /*
|C|C|C|

ABCCC

CCCAB
CCACB
CACCB
ACCCB

CCABC
CACBC
ACCBC

CABCC
ACBCC

ABCCC
    */

    // B.
    // こちらはAと違い、一つの塔の中で入れ替えが何パターンきくかというもの
    // 例えばABCが直列につながっているなら、ABCの1パターンしかないから1だけど、
    // AがB,Cそれぞれにつながっているなら、ABCとACBで2ということになり、
    // 塔ごとにこの数値を出してAの結果に乗算していく。
    //
    // しかしそもそもパターン数を計算するために、A,
    // Bと問題を分けたことを思い出す Bを計算するためには、その塔の中でA,
    // Bの計算をしなければいけない。 ↓つまり
    // N個のノードに対しN-1個の辺ということは、
    // 1. どの方向に行っても必ず終端に行きつき、
    // 2. 循環は決してしない
    // という特徴がある。よって、それぞれの終端からA, Bの処理を順にかけていき、
    // つながっていて計算できるノードを順に計算していけば、いいということ。
    // （Bの処理を行うためには、そのリンク先の計算が完了している必要があるが、
    // 　終端については難しい処理がなく、A = 1, B =
    // 1となるため完了と見ることができるので）

    int nodeCount = 0; // 既に処理したNode数
    u32 a = 1;         // Aの計算結果
    u32 b = 1;         // Bの計算結果
    auto ignoreScore = ignoreLink != nullptr ? ignoreLink->Score : nullptr;
    for (auto scoreAndCount : node->GetScores()) {
      auto count = scoreAndCount.second;
      auto score = scoreAndCount.first;
      if (ignoreScore == score) {
        count--;
      }
      for (; count > 0; count--) {

        // Aの処理
        // 差込処理をしていく。nCr
        // if (nodeCount > 0) {
        a = modulo.Mul(
            a, modulo.Ncr(nodeCount + score->NodeCount, score->NodeCount));

        nodeCount += score->NodeCount;

        b = modulo.Mul(b, score->PatternCount);
      }
    }

    // <NodeCount, PatternCount>
    return new LinkScore(nodeCount + 1, modulo.Mul(a, b));
  };

  // リンクを評価する
  auto evaluateLink = [&](Link *link) {
    // #define DEBUG_COMPLETE_ONE_LINK

    auto startNode = link->From;

#ifdef DEBUG
    cout << "evaluating ";
    link->Print();
    cout << "\\n";
#endif

    LinkScore *score;

    // ロングキーを生成
    auto &&longKey = link->GetLongKey(startNode);

    // このロングキーが既に登録されているか？
    auto it = linkKey2Score.find(longKey);
    if (it != linkKey2Score.end()) {
      // 既に登録されている
      // - リンクスコアの計算は必要ない。

#ifdef DEBUG
      // cout << "Existing link key: " << longKey << "\\n";
#endif

      score = it->second.get();
    } else {
      // まだ登録されていない
      // - リンクスコアを計算する

#ifdef DEBUG
      // cout << "Added new link key: " << longKey << "\\n";
#endif

      if (link->To->IsTip()) {
        // リンク先は末端
        linkKey2Score[longKey].reset(score = new LinkScore(1, 1));
      } else {
        // リンク先は末端ではない
        linkKey2Score[longKey].reset(
            score =
                combineLink(link->GetOtherSideOf(startNode), link->Reverse));
      }
    }

#ifdef DEBUG
    cout << " result: ";
    score->Print();
    cout << '\\n';
#endif

    startNode->AddScore(link->Score = score);
    return score;
  };

  // リンクの評価完了により、新しく評価できるようになった
  // リンクを探して全て評価予定キューに追加する
  auto addNextQueue = [&](Link *link) {
    auto startNode = link->From;

    // auto receiverLink = find(to->GetLinks().begin(), to->GetLinks().end(),
    // *link);

    // 完成したリンクをリストから除去
    // startNode->GetLinks()->erase(link);
    // to->GetLinks().erase(receiverLink);

    --startNode->IncompleteLinkCount;

#ifdef DEBUG
    remaining--;
#endif

    if (startNode->IncompleteLinkCount == 1) {
      // 未完成リンク = 1:
      // 未完成リンクの逆向きを、キューに追加
      // assert(startNode->GetLinks()->size() == 1);

      for (auto slink = startNode->GetLinks()->begin();
           slink != startNode->GetLinks()->end(); slink++) {
        if ((*slink)->Score == nullptr) {

#ifdef DEBUG
          cout << "Adding to queue: ";
          (*slink)->Reverse->Print();
          cout << '\\n';
#endif
          // auto& receiverLinks =
          // (*slink)->GetOtherSideOf(startNode)->GetLinks();
          // linkQueue.emplace((*slink)->GetOtherSideOf(startNode),
          // find(receiverLinks.begin(), receiverLinks.end(), *slink));
          linkQueue.push((*slink)->Reverse);
          break;
        }
      }
    } else if (startNode->IncompleteLinkCount == 0) {
      // 未完成リンク = 0:
      // 今回完成したリンク以外全てのリンクの逆向きをキューに追加
      for (auto rlink = startNode->GetReceiveLinks()->begin();
           rlink != startNode->GetReceiveLinks()->end(); rlink++) {

        if (*rlink == link->Reverse) {
          continue;
        }

#ifdef DEBUG
        cout << "Adding to queue: ";
        (*rlink)->Print();
        cout << '\\n';
#endif
        linkQueue.push(*rlink);
      }
    }

    // delete* link, * receiverLink;

    return;
  };

#ifdef DEBUG
  cout << "* Evaluating all links connected to end point ... *\\n";
#endif

  // 全ての末端に対するリンクを評価
  for (int i = 1; i <= nodeCount; i++) {
    auto &node = nodes[i];

    if (node.IsTip()) {
      assert(node.GetLinks()->size() == 1);
      auto it = node.GetReceiveLinks()->begin();

      evaluateLink(*it);
      addNextQueue(*it);
    }
  }

#ifdef DEBUG
#if 1
  PrintAllNodes(nodes, nodeCount + 1);
  cout << '\\n';
#endif
#if 1
  cout << "* Evaluating remaining nodes in queue ... *\\n";
  cout << "queue.size = " << linkQueue.size() << '\\n';
#endif
#endif

  // リンク先ノードの持つ（このリンクの逆以外の）他の全てのリンクの
  // 評価が終わっていれば、リンクの評価をする
  // 残り処理数がなくなるまで行う
  while (linkQueue.size() > 0) {

    auto link = linkQueue.front();
    evaluateLink(link);
    addNextQueue(link);

    linkQueue.pop();
  }

#ifdef DEBUG
  assert(remaining == 0);

  cout << "*Pre-calculation completed*\\n";
#if 0
  PrintAllNodes(nodes, nodeCount + 1);
#endif
#endif

  // 事前計算終了

  // ノードそれぞれにつき、そのノードを始点とした選び方のパターン数を出す
  // ＝そのノードの全リンクを結合する
  for (int i = 1; i <= nodeCount; i++) {

    unique_ptr<LinkScore> score(combineLink(&nodes[i], nullptr));
    cout << score->PatternCount << '\\n';
  }

  delete[] nodes;
}

#if 1
int main() {
  cin.tie(0);
  ios_base::sync_with_stdio(false);

  Start();
}
#endif


Error: TLE

Fixed submission:

#include "bits/stdc++.h"

//
// 関数結果をキャッシュする
//
// 関数呼び出し結果をキャッシュし、2回目以降
// 同じ引数リストで呼び出した場合は、実際に関数を呼び出すのではなく
// かわりにキャッシュした結果から返す
//
// 引数が同じなら得られる答えが常に同じかつ、1回の計算コストが高く、
// 同じ引数で何度も呼び出す予定がある関数に最適
//
// ただしメモリを山のように食う
// メモリだけではなく、速度も扱い方によっては速くも遅くもなりうるので
// 速度を見つつ導入するかどうか考えながら注意して使うこと

using namespace std;

using u32 = uint32_t;
using u64 = uint64_t;

template <typename T> class CachedFunction_u32_u32 {
public:
  CachedFunction_u32_u32(T (*const func)(u32, u32)) : func(func) {}

  ~CachedFunction_u32_u32() { Release(); }

  T Call(u32 a, u32 b) {
    u64 key;
    *((u32 *)&key) = a;
    *((u32 *)&key + 1) = b;

    auto it = results.find(key);
    if (it != results.end()) {
      return it->second;
    }

    return results[key] = func(a, b);
  }

  void Release() { results.clear(); }

private:
  T (*const func)(u32, u32);
  unordered_map<u64, T> results;
};

template <typename T> class CachedFunction_u64_u64 {
public:
  CachedFunction_u64_u64(T (*const func)(u64, u64)) : func(func) {}

  ~CachedFunction_u64_u64() { Release(); }

  T Call(u64 a, u64 b) {
    auto i = results.find(a);
    if (i != results.end()) {
      auto j = i->second->find(b);
      if (j != i->second->end()) {
        return j->second;
      }

      return (*(i->second))[b] = func(a, b);
    }

    results[a] = new unordered_map<u64, T>;
    return (*results[a])[b] = func(a, b);
  }

  void Release() {
    for (auto &r : results) {
      r.second->clear();
      delete r.second;
    }
    results.clear();
  }

private:
  T (*const func)(u64, u64);
  unordered_map<u64, unordered_map<u64, T> *> results;
};

template <typename T>
CachedFunction_u32_u32<T> GetCachedFunction(T (*const func)(u32, u32)) {

  // move未対応だから実際はコピーされてしまうが、
  // この関数自体を呼ぶ回数自体は少ないので（得られたObjをたくさん使うので）
  // さして問題はないだろう
  return move(CachedFunction_u32_u32<T>(func));
}

template <typename T>
CachedFunction_u64_u64<T> GetCachedFunction(T (*const func)(u64, u64)) {
  return move(CachedFunction_u64_u64<T>(func));
}

#include "bits/stdc++.h"

namespace FactorialLimit {
using namespace std;

constexpr uint32_t DivineDivisor = 1000000007;

using u32 = uint32_t;
using u64 = uint64_t;

constexpr u32 DivineDivide(u64 value) { return (u32)(value % DivineDivisor); }

constexpr u32 DivineDivideAvoid0(u64 value) {
  u32 ret = (u32)(value % DivineDivisor);
  return ret != 0 ? ret : DivineDivisor;
}

u32 GetCombinationCountModulo(u32 a, u32 b, u32 c);

u32 GetCombinationCountModulo(u32 a, u32 b);
} // namespace FactorialLimit

//
// https://atcoder.jp/contests/abc160/tasks/abc160_f
// のためのコード
//
// 組み合わせの公式の計算をし、その結果を指定した数で割り、あまりを求める関数を作る
//
// しかし割る前の数値が到底C++の数値型に収まらないレベルの巨大な数なので、
// それを何とか頑張って計算するために創意工夫する

namespace FactorialLimit {

using namespace std;

// 引数 n に対し階乗の計算 n(n-1)(n-2)...を行う場合、
// 何回の乗算まで max を超えずにできるかを計算する
template <typename A> A GetFactorialSafeMultiplyCount(A prm, A max) {

  if (prm > max) {
    return 0;
  }

  A cur = prm;
  for (A i = prm - 1; i > 1; i--) {
    A quotient = max / cur;
    // A remainder = max - prm * quotient; // max % quotient
    if (i > quotient) {
      return prm - 1 - i;
    }
    cur *= i;
  }

  // 階乗全ての計算を、max以下で完了できる
  return prm - 1;
}

void PrintFactorialSMC_UI64() {

  cout << "UINT64_MAX = " << UINT64_MAX << "\\n";

  uint32_t prev = 0;

  for (uint32_t i = UINT32_MAX; i >= 1; i--) {

    uint32_t cnt =
        (uint32_t)GetFactorialSafeMultiplyCount<uint64_t>(i, UINT64_MAX);
    if (cnt != prev) {
      prev = cnt;
      cout << cnt << "回 " << i << "から\\n";

      // 次の値の概算、少し多めに
      // - 後半かなりの誤差が出るので、最後に十分な数を足しておくこと！
      uint32_t next =
          ((uint32_t)pow((double)UINT64_MAX, 1.0 / (double)(cnt + 2))) + 50;
      if (next < i)
        i = next;
    }
  }
}

// 静的配列の要素数を求める。inline展開されるはず
// C++17以降はstd::size()で一発らしい
template <typename T, size_t N> constexpr size_t array_length(T (&)[N]) {
  return N;
}

constexpr uint32_t GetFactorialSMCFast(uint32_t value) {

  // 階乗を計算する際、uint64_t内でオーバーフローせずに乗算できる回数を
  // 求める際に使う配列。indexは可能な乗算回数を表していて、その回数乗算できる最大の数値が入っている
  // - 別途プログラムで計算しておきハードコードする
  constexpr array<uint64_t, 20> u64fcDivs{
      UINT64_MAX, 4294967295, 2642246, 65537, 7133, 1627, 568, 259, 142, 88,
      61,         45,         36,      30,    26,   24,   22,  21,  20,  20};
  constexpr int u64fcDivsLen = u64fcDivs.size();

  for (uint32_t i = 2; i < u64fcDivsLen; i++) {
    if (value > u64fcDivs[i]) {
      return i - 1;
    }
  }

  return value - 1;
}

// 階乗計算を、オーバーフローしないように小分けにuint64_t配列に入れていく場合、
// いくつの要素が必要になるのかを計算する
uint32_t GetVarCountForFactorial(uint32_t value) {

  uint32_t result = 0;
  for (auto i = value; i > 1; /*NOP*/) {
    auto cnt = GetFactorialSMCFast(i);
    result++;
    i -= cnt;
  }
  return result;
}
uint32_t GetVarCountForHalfFactorial(uint32_t value) {

  uint32_t result = 0;
  for (auto i = value; i > value / 2; /*NOP*/) {
    auto cnt = GetFactorialSMCFast(i);
    result++;
    i -= cnt;
  }
  return result;
}

// 最大公約数を求める
template <typename T> constexpr T GetGreatestCommonDivisor(T a, T b) {

  // cout << "gcd(" << a << "," << b << ") = ";

  if (b > a) {
    swap(a, b);
  }

  while (true) {
    T c = a % b;
    if (c == 0)
      break;

    a = move(b);
    b = move(c);
  }

  // cout << b << '\\n';

  return b;
}

// 下記関数で計算に使うコレクション
// vector<uint32_t> gccm_facts;
// uint32_t facts[200001];

// a 個の中からb 個選び出す組み合わせの数を求め、結果を c で割った余りを得る
// ただし余りが出なかった場合は c を返す
// - メモリ再確保によるオーバーヘッドをなくすため、あらかじめかなりの
//   サイズを確保している。コンテスト利用以外では実装を考えなおすこと
uint32_t GetCombinationCountModulo(uint32_t a, uint32_t b, uint32_t c) {
  if (b > a) {
    return 0;
  }

  // 組み合わせ（順列ではない）において a から b個選ぶのは、a から a-b
  // 個選ぶのと同じ
  if (b > a / 2) {
    b = a - b;
  }

  static auto ggcdCached = GetCachedFunction(GetGreatestCommonDivisor<u64>);

#if 1
  // 組み合わせの公式で分子になる階乗を、オーバーフローしない範囲でなるべく掛け合わせて入れていく配列
  // 分子配列と呼ぶことにする
  static uint64_t numerators[50003];
  uint64_t *numeratorsEnd = numerators;

  // 分子の階乗計算を、配列に小分けにして入れていく
  for (auto i = a, amb = a - b; i > amb; /*nop*/) {

    // 何回までオーバーフローせず乗算できるか
    auto cnt = GetFactorialSMCFast(i);

    auto m = max(amb, i - cnt - 1);

    *(numeratorsEnd) = i;
    for (auto j = i - 1; j > m; j--) {
      *(numeratorsEnd) *= j;
    }

    // cout << *(numeratorsEnd) << ',';

    numeratorsEnd++;

    i -= cnt + 1;
  }
#elif 0
  gccm_facts.reserve(b);
  for (auto i = a, amb = a - b; i > amb; i--) {
    gccm_facts.push_back(i);
  }
#elif 0
  auto *factsEnd = facts;
  for (auto i = a, amb = a - b; i > amb; i--) {
    *(factsEnd++) = i;
  }
#endif

  // 分母の階乗
  // A.
  // こちらも分子と同じように配列に小分けにしていき、後でそれらを除数にするか、
  // B. それとも一つひとつ分子配列から割っていくか
  // の2通りが考えられる
  //
  // Aの場合は除算の量が少なく済みそうだが、どの分子配列からも割り切れないパターンが出てくる。
  // その際はユークリッド互除法を使って割れる分だけ割っていく処理が必要になる。
  //
  // Bはおそらくそのようなパターンは少ない
  // （ただし起こりうることは確認済み。 a = 71, b = 34 で確認）
  // が、そもそも数字の数だけ除算するので単純に計算量が増える
  //
  // どちらの方が良いか、はたまたよりよい方法が思いつくか。
  //
  // とりあえず実装が楽そうなBでやってみる。
  // ...
  // 結果。遅すぎ
  // なのでAで作ってみる

#if 1
  // 実装 A

  auto *npCur = numerators;

  // 分子である除数を計算していく
  // こちらは、計算するごとに除算していくので配列は必要ない
  for (auto i = b; i > 1; /*nop*/) {
    // 何回までオーバーフローせず乗算できるか
    auto cnt = GetFactorialSMCFast(i);

    auto m = max(1U, i - cnt - 1);

    uint64_t div = i;
    for (auto j = i - 1; j > m; j--) {
      div *= j;
    }

    // 分子配列内で、割り切れる被除数を探す
    // div を使い切るまで
    for (auto *npReserved = npCur++; /*NOP*/; npCur++) {
      if (npCur == numeratorsEnd) {
        npCur = numerators;
      }

      // 最大公約数で両者を割る
      auto gcd =

          // ここはキャッシュするとかえって遅くなるのでしない
          //*npCur < div ? ggcdCached.Call(*npCur, div) : ggcdCached.Call(div,
          //*npCur)

          GetGreatestCommonDivisor(*npCur, div);
      *npCur /= gcd;
      if (div == gcd)
        break;
      div /= gcd;

      if (npCur == npReserved) {
        // throw runtime_error("");
      }
    }

    i -= cnt + 1;
  }

#elif 1

  // 実装 B
  auto *npCur = numerators;
  for (auto i = b; i > 1; i--) {
    uint64_t div = i;

    // 分子配列内で、割り切れる被除数を探す
    for (auto *npReserved = npCur++; /*NOP*/; npCur++) {
      if (npCur == numeratorsEnd) {
        npCur = numerators;
      }

      // 最大公約数で両者を割る
      auto gcd = GetGreatestCommonDivisor(*npCur, div);
      *npCur /= gcd;
      div /= gcd;
      if (div == 1)
        break;
    }
  }
#endif

  // 分子配列を掛け合わせる。
  uint64_t result = 1;
  for (auto *p = numerators; p != numeratorsEnd; p++) {
    // cout << *p << ',';
    result = DivineDivideAvoid0(result * *p);
  }

  // cout << "GCCM(" << a << "," << b << "," << c << ") = " << result << '\\n';

  // uint32_t で割った余りになっているので、オーバーフローはない
  return (uint32_t)result;
}

uint32_t GetCombinationCountModulo(u32 a, u32 b) {
  return GetCombinationCountModulo(a, b, DivineDivisor);
}

template <uint32_t LENGTH> struct PrimeFactorization {
  constexpr PrimeFactorization() : map() {
    for (uint32_t i = 0; i < LENGTH; i++) {
    }
  }
  unordered_map<uint32_t, uint32_t> map[LENGTH];
};

// 指定した個数の素数を計算する
// - 呼び出し側で constexpr を付けることでコンパイル時計算になる。ただしその場合
//   引数が大きいとエラー。100くらいならいける。1000はコンパイルエラーだった
// - constexpr
// つけなければ大きい数もいける（実行時処理になる。大きすぎると演算時間膨大）
template <uint32_t LENGTH> struct PrimeNumbers {
  constexpr PrimeNumbers() : arr() {

    uint32_t c = 2;

    for (uint32_t i = 0; i < LENGTH; i++) {

      while (true) {
        // 現在の数を割り切れるエントリがあるか探す
        bool isPrime = true;
        for (uint32_t j = 0; j < i; j++) {
          if (c % arr[j] == 0) {
            isPrime = false;
            break;
          }
        }

        if (isPrime) {
          arr[i] = c++;
          break;
        }

        c++;
      }
    }
  }
  uint32_t arr[LENGTH];
};

// 素数を列挙する
void EnumeratePrimeNumbers() {

  // 素数列挙
  // - 自分の環境だと100くらいならいける。1000はコンパイルエラーだった
#if 0
    constexpr
#endif
  auto pn = PrimeNumbers<20000U>();
  auto pnarr = pn.arr;
  for (int i = 0; i < array_length(pn.arr); i++) {
    cout << pnarr[i] << ',';
  }
}

void Start() {

  PrintFactorialSMC_UI64();

  // F問題のNの最大は2 * 10^5 。少し多めにとるd
  constexpr uint32_t max = 200010;
  auto c = GetVarCountForHalfFactorial(max);
  cout << max << "の階乗（半分まで）の小分けに必要な配列数（概算） = " << c
       << '\\n';

  constexpr uint32_t maxHalf = max / 2;
  auto c2 = GetVarCountForFactorial(maxHalf);
  cout << maxHalf << "の階乗の小分けに必要な配列数（概算） = " << c2 << '\\n';

  // 電卓:184,756
  // cout << GetCombinationCountModulo(20, 10, DivineDivisor) << '\\n';
  // 電卓:30,045,015
  // cout << GetCombinationCountModulo(30, 10, DivineDivisor) << '\\n';
  // 電卓:847,660,528
  // cout << GetCombinationCountModulo(40, 10, DivineDivisor) << '\\n';

  // 電卓:309,335,270
  // cout << GetCombinationCountModulo(100, 10, DivineDivisor) << '\\n';

  // cout << GetCombinationCountModulo(71, 34, DivineDivisor) << '\\n';

  // ? (電卓や外部サイトでの確認をしていないので、正しいか分からない）
  // cout << GetCombinationCountModulo(200000, 100000, DivineDivisor) << '\\n';

  // CachedFunction のテスト
  auto gccmCached = GetCachedFunction(GetCombinationCountModulo);
  for (int i = 0; i < 3; i++) {
    cout << (i + 1) << "回目の呼び出し開始...\\n";
    cout << "完了: 返り値 = " << gccmCached.Call(60000, 30000) << '\\n';
  }

  // EnumeratePrimeNumbers();
}
} // namespace FactorialLimit

#if 0
int main() {
  FactorialLimit::Start();
  return 0;
}
#endif

using namespace std;

// #define DEBUG

using u64 = uint64_t;
using u32 = uint32_t;
using ll = long long;

class Link;

// https://atcoder.jp/contests/abc160/submissions/11270339
// イニシャルKさんのコードにあった謎のクラスModuloを移植
class Modulo {
public:
  Modulo(u32 n) {
    facs = new u32[n + 1];
    facs[0] = 1;
    for (u32 i = 1; i <= n; i++) {
      facs[i] = Mul(facs[i - 1], i);
    }
  }
  u32 Mul(u32 a, u32 b) { return (u32)(((u64)a) * b % DivineDivisor); }
  u32 Fac(u32 n) { return facs[n]; }
  u32 Pow(u32 a, u32 m) {
    switch (m) {
    case 0:
      return 1;
    case 1:
      return a;
    default:
      u32 p1 = Pow(a, m / 2);
      u32 p2 = Mul(p1, p1);
      return ((m % 2 == 0) ? p2 : Mul(p2, a));
    }
  }
  u32 Div(u32 a, u32 b) { return Mul(a, Pow(b, DivineDivisor - 2)); }
  u32 Ncr(u32 n, u32 r) {
    if (n < r) {
      return 0;
    }
    if (n == r) {
      return 1;
    }
    auto res = facs[n];
    res = Div(res, facs[r]);
    res = Div(res, facs[n - r]);
    return res;
  }

private:
  constexpr static u32 DivineDivisor = 1000000007;
  u32 *facs;
};

// ノード: 木のそれぞれの頂点を表す
class Node {
public:
  list<Link *> Links;
  list<Link *> ReceiveLinks;

  int IncompleteLinkCount = 0;

  void MutualLink(Node *To);

  // 末端か
  bool IsTip() { return Links.size() < 2; }

#ifdef DEBUG
  int Id;
  static int NextId;

  Node() { Id = NextId++; }

  void Print();
#endif
};

// リンク: ノード間の繋がりを表す。指向性
class Link {
public:
  Link(Node *From, Node *To) : From(From), To(To) {}

  using ShortKeyType = u32;

  Node *From, *To;
  Link *Reverse = nullptr;

  ShortKeyType ShortKey = 0;

  bool IsShortKeyDetermined() { return ShortKey != 0; }

  // ロングキーを生成
  string GetLongKey() {
    string result = "";

    // 接続先ノードを始点とするリンクのうち、このリンクの逆向きを
    // 除く全てのリンクのショートキーにより決定する

    auto links = To->Links;
    shortKeys.reserve(links.size() - 1);
    shortKeys.clear();

    for (auto link : links) {
      if (link == this->Reverse) {
        continue;
      } else if (!link->IsShortKeyDetermined()) {
        // ショートキーの決定していないリンクがある:
        return "";
      } else {
        shortKeys.push_back(link->ShortKey);
      }
    }

    if (shortKeys.size() == 0) {
      // 末端
      result = "T";
    } else {
      // 末端ではない
      sort(shortKeys.begin(), shortKeys.end());
      for (auto shortKey : shortKeys) {
        result += to_string(shortKey) + "+";
      }
    }

    return result;
  }

#ifdef DEBUG
  void Print();
#endif

private:
  static vector<ShortKeyType> shortKeys;
};
vector<Link::ShortKeyType> Link::shortKeys;

// リンクのスコア（計算済パラメータ）
class LinkScore {
public:
  LinkScore(u32 NodeCount = 0, u32 PatternCount = 0)
      : NodeCount(NodeCount), PatternCount(PatternCount) {}
  u32 NodeCount;
  u32 PatternCount;
};
#ifdef DEBUG
int Node::NextId = 0;

void Node::Print() {
  cout << "Node#" << Id << ":\\n";
  for (auto link : Links) {
    cout << " ";
    link->Print();
  }
  // cout << '\\n';
};

void Link::Print() {
  cout << "Link " << ShortKey << "/" << GetLongKey() << " " << From->Id << "->"
       << To->Id << '\\n';
};
#endif

#ifdef DEBUG
void PrintAllNodes(Node *nodes, int count) {
  for (int i = 0; i < count; i++) {
    nodes[i].Print();
  }
}
#endif

void Node::MutualLink(Node *To) {
  Links.emplace_back(new Link(this, To));
  auto sendLink = *Links.rbegin();
  To->Links.emplace_back(new Link(To, this));
  auto receiveLink = *To->Links.rbegin();

  sendLink->Reverse = receiveLink;
  receiveLink->Reverse = sendLink;

  To->ReceiveLinks.push_back(sendLink);
  IncompleteLinkCount++;

  ReceiveLinks.push_back(receiveLink);
  To->IncompleteLinkCount++;
}

void Start() {
  int nodeCount;
  cin >> nodeCount;

  Node *nodes = new Node[nodeCount + 1];

  for (int i = 1; i <= nodeCount - 1; i++) {
    int a, b;
    cin >> a >> b;
    nodes[a].MutualLink(&nodes[b]);
  }

#if defined(DEBUG) && 0
  cout << "*Initial State*\\n";
  PrintAllNodes(nodes, nodeCount + 1);
  cout << '\\n';
#endif

  // LinkのLongKey/ShortKey対応表
  unordered_map<string, Link::ShortKeyType> longKey2ShortKey;

  // LinkのShortKey/LinkScore対応表
  unordered_map<Link::ShortKeyType, LinkScore> shortKey2LinkScore;

  // 次に割り振るShortKey
  u32 nextShortKey = 1;

  // 処理必要なリンク残り
  int remaining = (nodeCount - 1) * 2;

  // 計算予定のリンク
  queue<Link *> linkQueue;

  // Modulo
  Modulo modulo(200000);

  // キャッシュされた関数呼び出し
  auto gccmCached =
      GetCachedFunction(FactorialLimit::GetCombinationCountModulo);

  // リンクを結合する:
  // 複数のリンクのノード数、パターン数を合わせた
  // ノード数（ただ足せばいい）、パターン数（これが大変）を計算する
  auto combineLink = [&](const Node *node,
                         const Node *ignoreLinkReceiver = nullptr) {
    // 一つのノードから見たパターン数の計算は、以下の2つの問題に分けられる。
    // A.どのリンク先（の集合）から選ぶか
    // B.それぞれのリンク先（の集合）について、どれから選ぶか
    // AとBそれぞれのパターン数を乗算すればいいはず。

    // A.
    // リンク先集合のノード数がたとえば2, 2, 1 なら
    // 積み木が2, 2,
    // 1個積みあがって3つの塔になっていて、毎回一つ上の積み木を取り出す。
    // どの塔から順に取り出すか、という選び方のパターンを計算すればいい。
    //
    // 左から順にA, B, Cの塔とすると
    // たとえばBAACBのような取り出し順がある。
    //
    // *計算法
    // 左から順に処理する。上の例で行く
    // まずAが2つ並んでいるとする。AA
    // これにBを2つ、順に差し込んでいく。AABB, BAABなど
    // 既に並んでいる数をp, 差し込む数をqとすると
    // 差し込める箇所はp + 1個。p + 1個から q 個選びだす組み合わせの公式でいける
    // そうして次はCを差し込み、また公式を使う
    // これを、塔がなくなるまで繰り返す
    // ...
    // と思ったが上の方法ではだめだった。やり直す。
    // A B CCC この例を考える。
    // B を A に入れる方法は |A| の2パターン ...[1]
    // CをABに順に入れていく。1つ目を入れる位置は|A|B|の3か所
    // しかし2個目はCが追加されているので、|A|B|C|の4か所になる
    // つまり3個追加するには、3*4*5。Cは区別しないので、Cの順番のパターンである3*2*1で割る
    // よって答えは10。 [1]をかけると20。
    // これはA B CCC の並び順のパターン数に等しい。よってこちらの計算法を使う。
    // ...
    // 既に並んでいる数を p 個、これから差し込む数を q 個とすると
    // p + q 個 から q 個選び出す組み合わせの公式でいける。きっといける

    /*
|C|C|C|

ABCCC

CCCAB
CCACB
CACCB
ACCCB

CCABC
CACBC
ACCBC

CABCC
ACBCC

ABCCC
  */

    // B.
    // こちらはAと違い、一つの塔の中で入れ替えが何パターンきくかというもの
    // 例えばABCが直列につながっているなら、ABCの1パターンしかないから1だけど、
    // AがB,Cそれぞれにつながっているなら、ABCとACBで2ということになり、
    // 塔ごとにこの数値を出してAの結果に乗算していく。
    //
    // しかしそもそもパターン数を計算するために、A,
    // Bと問題を分けたことを思い出す Bを計算するためには、その塔の中でA,
    // Bの計算をしなければいけない。 ↓つまり
    // N個のノードに対しN-1個の辺ということは、
    // 1. どの方向に行っても必ず終端に行きつき、
    // 2. 循環は決してしない
    // という特徴がある。よって、それぞれの終端からA, Bの処理を順にかけていき、
    // つながっていて計算できるノードを順に計算していけば、いいということ。
    // （Bの処理を行うためには、そのリンク先の計算が完了している必要があるが、
    // 　終端については難しい処理がなく、A = 1, B =
    // 1となるため完了と見ることができるので）

    int nodeCount = 0; // 既に処理したNode数
    ll a = 1;          // Aの計算結果
    ll b = 1;          // Bの計算結果
    for (auto receiverLink : node->Links) {
      if (receiverLink->To == ignoreLinkReceiver)
        continue;

      auto &score = shortKey2LinkScore[receiverLink->ShortKey];

      // Aの処理
      if (nodeCount > 0) {
        // 差込処理をしていく。組み合わせの公式

#if 0
        // キャッシュ最適化のため等価な呼び出しをまとめたいので、
        // 予め第2引数 < 第1引数 / 2 にしておく。
        if (nodeCount > score.NodeCount) {
          a *=
            gccmCached.Call(
              nodeCount + score.NodeCount, nodeCount
            );
        }
        else {
          a *=
            //FactorialLimit::GetCombinationCountModulo(

            gccmCached.Call(
              nodeCount + score.NodeCount, score.NodeCount

              //, FactorialLimit::DivineDivisor

            );
        }
#else
        a *= modulo.Ncr(nodeCount + score.NodeCount, score.NodeCount);
#endif
        a = FactorialLimit::DivineDivideAvoid0(a);
      }

      nodeCount += score.NodeCount;

      b = b * score.PatternCount;
      b = FactorialLimit::DivineDivideAvoid0(b);
    }

    // <NodeCount, PatternCount>
    return LinkScore(nodeCount + 1, FactorialLimit::DivineDivideAvoid0(a * b));
  };

  // リンクを評価する
  auto evaluateLink = [&](Link *link) {
    // #define DEBUG_COMPLETE_ONE_LINK

    // ロングキーを生成
    auto longKey = link->GetLongKey();

    // このロングキーが既に登録されているか？
    auto itShortKey = longKey2ShortKey.find(longKey);
    if (itShortKey != longKey2ShortKey.end()) {
      // 既に登録されている
      // - リンクスコアの計算は必要ない。

#ifdef DEBUG
      link->Print();
      cout << longKey << "は登録済\\n";
#endif

      link->ShortKey = (*itShortKey).second;
    } else {
      // まだ登録されていない
      // - リンクスコアを計算する

      link->ShortKey = nextShortKey++;

#ifdef DEBUG
      link->Print();
      cout << longKey << "を登録します\\n";

      cout << link->ShortKey << "?\\n";
#endif

      longKey2ShortKey[longKey] = link->ShortKey;

      if (link->To->IsTip()) {
        // リンク先は末端
        shortKey2LinkScore[link->ShortKey] = LinkScore(1, 1);
      } else {
        // リンク先は末端ではない
        shortKey2LinkScore[link->ShortKey] = combineLink(link->To, link->From);
      }
    }

    // このリンクの評価完了により、新たに評価ができるようになった
    // リンクを全て評価予定キューに追加する

    // NOTE: 重複してキューに追加される可能性があるか？多分ないと思うけど…

    Node *from = link->From;
    remaining--;
    --from->IncompleteLinkCount;

    if (from->IncompleteLinkCount == 1) {
      // 未完成リンク = 1:
      // 未完成リンクの逆向きを、キューに追加
      for (auto slink : from->Links) {
        if (!slink->IsShortKeyDetermined()) {
#if defined(DEBUG) && defined(DEBUG_COMPLETE_ONE_LINK)
          cout << "onCompleteOneLink\\n ";
          link.Print();
          cout << "Adding a link to queue: ";
          slink.Reverse->Print();
#endif
          linkQueue.push(slink->Reverse);
          break;
        }
      }
    } else if (from->IncompleteLinkCount == 0) {
      // 未完成リンク = 0:
      // 今回完成したリンクの逆向き以外を、全てキューに追加
      auto reverse = link->Reverse;
#if defined(DEBUG) && defined(DEBUG_COMPLETE_ONE_LINK)
      cout << "onCompleteOneLink\\n ";
      link.Print();
      cout << "Adding a link to queue: ";
#endif
      for (auto rlink : from->ReceiveLinks) {
        if (rlink == reverse)
          continue;

#if defined(DEBUG) && defined(DEBUG_COMPLETE_ONE_LINK)
        rlink->Print();
#endif
        linkQueue.push(rlink);
        // rlink->PatternCount = -1;	// TODO:重複回避
      }
    }
  };

  // 全ての末端に対するリンクを評価
  for (int i = 1; i <= nodeCount; i++) {
    auto &node = nodes[i];
    for (auto link : node.Links) {
      if (link->To->IsTip()) {
        evaluateLink(link);
      }
    }
  }

#ifdef DEBUG
#if 1
  cout << "*Nodes After Calculating Tips*\\n";
  PrintAllNodes(nodes, nodeCount + 1);
  cout << '\\n';
#endif
#if 1
  cout << "queue.size = " << linkQueue.size() << '\\n';

  // cout << "GC=" << GetCombinationPatternCount(6, 4);
#endif
#endif

  // return 0;

  // ノードAのもつリンクBについて、リンク先ノードの持つリンクのうち、
  // Aへのリンクをのぞいたすべてのリンクの計算が完了している場合、リンクBの計算をする
  // これを残り処理数がなくなるまで行う
  while (linkQueue.size() > 0) {

    auto link = linkQueue.front();

    evaluateLink(link);

#ifdef DEBUG
#if 1
    cout << "Resolved Link: ";
    link->Print();
#endif
#endif

    linkQueue.pop();
  }

#ifdef DEBUG
  if (remaining != 0) {
    // ここに来ているときは、何かしら想定外のバグが発生している
    // throw runtime_error("UWAAAAA");
    cout << "マズいことになった…\\n";
  }

#if 1
  PrintAllNodes(nodes, nodeCount + 1);
#endif
#endif

  // 事前計算終了

  // ノードそれぞれにつき、そのノードを始点とした選び方のパターン数を出す
  // ＝そのノードの全リンクを結合する
  for (int i = 1; i <= nodeCount; i++) {

    cout << FactorialLimit::DivineDivide(
                combineLink(&nodes[i], nullptr).PatternCount)
         << '\\n';
  }

  delete[] nodes;
}

#if 1
int main() {
  cin.tie(0);
  ios_base::sync_with_stdio(false);

  Start();
}
#endif