#include <bits/stdc++.h>
using namespace std;
using lint = long long int;
struct fast_ios { fast_ios(){ cin.tie(0); ios::sync_with_stdio(false); cout << fixed << setprecision(20); }; } fast_ios_;
#define FOR(i, begin, end) for(int i=(begin);i<(end);i++)
#define IFOR(i, begin, end) for(int i=(end)-1;i>=(begin);i--)
#define REP(i, n) FOR(i,0,n)
#define IREP(i, n) IFOR(i,0,n)

// 頂点数Vmax以下（0-indexed）でV頂点V-1辺の木

template <int Vmax>
struct Tree
{
    int V; // 頂点数
    array<vector<int>, Vmax> to; // 隣接
    Tree() : V(1) {}

    void add_edge(int v1, int v2)
    {
        to[v1].push_back(v2);
        to[v2].push_back(v1);
        V++;
    }

    // 根を固定した木としての構造
    int root;
    array<int, Vmax> par;         // 親ノード par[root] = -1
    array<vector<int>, Vmax> chi; // 子の一覧

    void fixroot(int r)
    {
        root = r;
        par.fill(-1);
        for (auto &vec : chi) vec.clear();
        queue<int> vs;
        vs.push(root);
        while (!vs.empty())
        {
            int now = vs.front();
            vs.pop();
            for (auto nxt : to[now]) if (nxt != par[now])
            {
                if (par[nxt] == -1)
                {
                    par[nxt] = now;
                    chi[now].push_back(nxt);
                    vs.push(nxt);
                }
            }
        }
    }


    //// 重心列挙 ////
    array<int, Vmax> subtreesz; // 各点をrootとする部分木のサイズ
    int buildSubtreeSize(int now, bool init)
    {
        if (init) subtreesz.fill(0);
        subtreesz[now] = 1;
        for (auto nxt : chi[now]) subtreesz[now] += buildSubtreeSize(nxt, false);
        return subtreesz[now];
    }
    vector<int> centroids; // 重心（その点を消すと残る各部分木の要素数が半分以下になる点）列挙
    void detectCentroid()
    {
        centroids.clear();
        REP(v, V)
        {
            bool flg = true;
            if (V - subtreesz[v] > V / 2) flg = false;
            for (auto nxt : chi[v]) if (subtreesz[nxt] > V / 2) flg = false;
            if (flg) centroids.push_back(v);
        }
    }
};

template <int mod>
struct ModInt
{
    using lint = long long;
    int val;
    ModInt() : val(0) {}
    void _setval(lint v) { v = (v % mod) + mod; val = v >= mod ? v - mod : v; }
    ModInt(lint v) { _setval(v); }
    ModInt operator+(const ModInt &x) const { return ModInt((lint)val + x.val); }
    ModInt operator-(const ModInt &x) const { return ModInt((lint)val - x.val); }
    ModInt operator*(const ModInt &x) const { return ModInt((lint)val * x.val); }
    ModInt operator/(const ModInt &x) const { return ModInt((lint)val * x.inv().val); }
    ModInt operator-() const { return ModInt(-val); }
    ModInt &operator+=(const ModInt &x) { return *this = *this + x; }
    ModInt &operator-=(const ModInt &x) { return *this = *this - x; }
    ModInt &operator*=(const ModInt &x) { return *this = *this * x; }
    ModInt &operator/=(const ModInt &x) { return *this = *this / x; }
    bool operator==(const ModInt &x) { return val == x.val; }
    bool operator!=(const ModInt &x) { return val != x.val; }
    friend ostream &operator<<(ostream &os, const ModInt &x) { os << x.val;  return os; }

    lint power(lint n) const {
        ModInt ans(1), tmp(val);
        while (n) {
            if (n & 1) ans *= tmp;
            tmp *= tmp;
            n /= 2;
        }
        return ans.val;
    }
    ModInt inv() const { return this->power(mod - 2); }
    
    ModInt fac() const {
        static vector<ModInt> facs;
        int l0 = facs.size();
        if (l0 > this->val) return facs[this->val];

        facs.resize(this->val + 1);
        for (int i = l0; i <= this->val; i++) facs[i] = (i == 0 ? 1 : facs[i - 1] * i);
        return facs[this->val];
    }

    ModInt nCr(const ModInt &r) const {
        if (this->val < r.val) return ModInt(0);
        return this->fac() / ((*this - r).fac() * r.fac());
    }
};
using mint = ModInt<1000000007>;

constexpr lint MOD = 1000000007;
lint power(lint x, lint n)
{
    lint ans = 1;
    while (n>0)
    {
        if (n & 1) (ans *= x) %= MOD;
        (x *= x) %= MOD;
       n >>= 1;
    }
   return ans;
}
vector<lint> fac, facInv, inv;
void facInit(int nmax)
{
    fac = facInv = inv = vector<lint>(nmax + 1, 1);
    for (int i = 2; i <= nmax; i++)
    {
        fac[i] = fac[i-1] * i % MOD;
        inv[i] = MOD - inv[MOD%i] * (MOD/i) % MOD;
        facInv[i] = facInv[i-1] * inv[i] % MOD;
    }
}
lint nCr(int n, int r)
{
    if (n<r || r<0) return 0;
    return (fac[n] * facInv[r] % MOD) * facInv[n-r] % MOD;
}

int N;


int main()
{
    Tree<5000> tree;
    cin >> N;
    REP(_, N - 1) {
        int x, y;
        cin >> x >> y;
        tree.add_edge(x - 1, y - 1);
    }
    tree.fixroot(0);
    tree.buildSubtreeSize(0, true);
    tree.detectCentroid();

    if (tree.centroids.size() == 2)
    {
        cout << mint(N / 2).fac() * mint(N / 2).fac() << endl;
        return 0;
    }

    int root = tree.centroids[0];
    tree.fixroot(root);
    tree.buildSubtreeSize(root, true);
    vector<lint> res;
    for (auto v : tree.chi[root]) res.push_back(tree.subtreesz[v]);
    sort(res.begin(), res.end());
    // for (auto v : res) cerr << v << endl;

    vector<lint> dp[2]; // dp[d][i] 違反の個数の偶奇がdで行先未定がi点のときの現状のパターン数
    dp[0] = {0, 1};
    dp[1] = {0, 0};
    facInit(5000);
    for (auto v : res)
    {
        vector<lint> dp_new[2];
        int len_prev = dp[0].size();
        REP(d, 2) dp_new[d].assign(len_prev + v, 0);
        REP(inow, len_prev) REP(d, 2) REP(inxt, v + 1)
        {
            (dp_new[d ^ (inxt & 1)][inow + v - inxt] += dp[d][inow] * nCr(v, inxt) % MOD * fac[inxt] % MOD * nCr(v, inxt)) %= MOD;
        }
        REP(d, 2) dp[d] = dp_new[d];
    }
    lint ans = 0;
    REP(i, dp[0].size()) {
        (ans += (dp[0][i] - dp[1][i] + MOD) * fac[i]) %= MOD;
    }
    cout << ans << endl;
}


Measured memory_bytes: 1280