牛客小白月赛55 F:至至子的公司排队

题目链接

思路:

很显然,公司关系构成了一棵树,该题实质求子树的拓扑序的方案数。考虑树形dp。
f [ u ] f[u] f[u]表示以 u u u为子树的拓扑序的数量。
转移: f [ u ] = ( s z [ u ] − 1 ) ! ∗ ∑ y s o n f [ j ] s z [ j ] ! f[u]=(sz[u]-1)! *\sum_{y}^{son}\frac{f[j]}{sz[j]!} f[u]=(sz[u]1)!ysonsz[j]!f[j]
对于 n n n个公司,我们建一个虚点作为它们的根。

code:

template <typename T> void inline read(T& x) {
    T f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}
template <typename T> void inline pr(T x) {
    if (x < 0) { putchar('-'); pr(-x); return; }
    if (x >= 10) pr(x / 10);
    putchar((x % 10) + '0');
}
template <typename T> T inline qpow(T a, T b, T mod) {
    T ans = 1;
    for (; b; b >>= 1, a = a * a % mod) if (b & 1) ans = ans * a % mod;
    return ans;
}
//----------------------------------------------------------------------------------------//
template<int m>
struct modint{
	unsigned int x;
	constexpr modint() noexcept : x(){}
	template<typename T>
	constexpr modint(T x_) noexcept : x((x_ %= m) < 0 ? x_ + m : x_) {}
	constexpr unsigned int val() const noexcept { return x; }
	constexpr modint &operator ++() noexcept { if(++x == m) x = 0; return *this; }
	constexpr modint &operator --() noexcept { if(x == 0) x = m; --x; return *this; }
	constexpr modint operator ++(int) noexcept { modint res = *this; ++ *this; return res; }
	constexpr modint operator --(int) noexcept{ modint res = *this;-- *this; return res; }
	constexpr modint &operator += (const modint &a) noexcept{ x += a.x; if(x >= m) x -= m; return *this; }
	constexpr modint &operator -= (const modint &a) noexcept{ if(x < a.x) x += m; x -= a.x; return *this; }
	constexpr modint &operator *= (const modint &a) noexcept{ x = (unsigned long long)x * a.x % m; return *this;}
	constexpr modint &operator /= (const modint &a) noexcept{ return *this *= a.inv(); }
	constexpr modint operator +() const noexcept { return *this; }
	constexpr modint operator -() const noexcept { return modint()-*this; }
	constexpr modint pow(long long n) const noexcept {
		if(n < 0) return pow(-n).inv();
		modint x = *this, r = 1;
		for(; n; x *= x,n >>= 1) if(n & 1) r *= x;
		return r;
	}
	constexpr modint inv() const noexcept {
		int s = x, t = m, x = 1, u = 0;
		while(t) {
			int k = s / t; s -= k * t;
			swap(s,t); x -= k * u;
			swap(x,u);
		}
		return modint(x);
	}
	friend constexpr modint operator + (const modint &a, const modint &b) { return modint(a) += b; }
	friend constexpr modint operator - (const modint &a, const modint &b) { return modint(a) -= b; }
	friend constexpr modint operator * (const modint &a, const modint &b) { return modint(a) *= b; }
	friend constexpr modint operator / (const modint &a, const modint &b) { return modint(a) /= b; }
	friend constexpr bool operator == (const modint &a, const modint &b) { return a.x == b.x; }
	friend constexpr bool operator != (const modint &a, const modint &b) { return a.x != b.x; }
	friend ostream &operator << (ostream &os,const modint &a){ return os << a.x;}
	friend istream &operator >> (istream &is,modint &a){ long long v; is >> v; a = modint(v); return is; }
};
using mint = modint<1000000007>;
// using mint = modint<998244353>;
namespace Combine{
	const int Combine_max = 3e5 + 50;
	mint fac[Combine_max];
	void init() { fac[0] = 1; for (int i = 1; i < Combine_max; ++ i) fac[i] = fac[i - 1] * i; }
	mint A(int n, int m) { return fac[n] / fac[n - m]; }
	mint C(int n, int m) { return fac[n] / (fac[n - m] * fac[m]); }
	mint ksm(mint x, int exp){
		mint res = 1;
		for (; exp; x *= x, exp >>= 1) if (exp & 1) res *= x;
		return res;
	}
}
using namespace Combine;

const int N = 2e5 + 10, M = N * 2, mod = 1e9 + 7;

int h[N], e[M], ne[M], idx;
int n, sz[N];
mint f[N];
void add(int a, int b) {
    e[idx] = b; ne[idx] = h[a]; h[a] = idx++;
}
void dfs(int u, int fa) {
    sz[u] = 1;
    f[u] = 1;
    for(int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];
        if(j == fa) continue;
        dfs(j, u);
        sz[u] += sz[j];
        // mint x = sz[j];
        f[u] = f[u] * f[j] * ksm(fac[sz[j]], mod - 2);
    }
    f[u] *= fac[sz[u] - 1];
}
signed main() {
#ifdef JANGYI
    freopen("input.in", "r", stdin);
    freopen("out.out", "w", stdout);
#endif
	init();
    read(n);
    mint ans = 1;
    int cnt = 0;
    rep(i, 1, n) {
        idx = 0;
        int m; read(m);
        cnt += m;
        rep(i, 0, m) h[i] = -1, sz[i] = 0, f[i] = 0;
        rep(i, 2, m) {
            int x; read(x);
            add(x, i);
        }
        dfs(1, 0);
        // D(f[1])
        ans *= f[1] * ksm(fac[m], mod - 2);
        // cout << ksm(m, mod - 2);
    }
    // cout << fac[cnt] << '\n';
    ans *= fac[cnt];
   
    cout << ans << '\n';
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值