zoukankan      html  css  js  c++  java
  • 【YbtOJ#10053】合影队形

    题目

    题目链接:http://noip.ybtoj.com.cn/problem/20053

    思路

    将被敬仰的人向敬仰他的人连一条有向边,那么如果存在环显然无解。
    否则由于每个点入度最多为 \(1\),所以形成了一棵树形结构。
    \(f[x]\) 表示 \(x\) 子树内排序的方案数。考虑加入一棵子树时,\(x\) 依然排在最右,剩余位置填给任意的人,方案数为

    \[f'[x]=f[x]\times f[v]\times \binom{size[x]+size[v]-1}{size[v]} \]

    时间复杂度 \(O(n\log \operatorname{MOD})\)
    由于出题人很不良心地要求每次模数都不一样,所以必须每次预处理阶乘和逆元。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const ll N=500010;
    ll Q,n,m,MOD,tot,head[N],deg[N],cnt[N],size[N],fac[N];
    ll ans,f[N];
    
    void prework()
    {
    	memset(head,-1,sizeof(head));
    	memset(deg,0,sizeof(deg));
    	memset(cnt,0,sizeof(cnt));
    	tot=0; ans=fac[0]=1;  
    	for (ll i=1;i<=n;i++)
    		fac[i]=fac[i-1]*i%MOD;
    }
    
    struct edge
    {
    	ll next,to;
    }e[N];
    
    void add(ll from,ll to)
    {
    	e[++tot].to=to;
    	e[tot].next=head[from];
    	head[from]=tot;
    }
    
    bool topsort()
    {
    	queue<int> q;
    	for (ll i=1;i<=n;i++)
    		if (!deg[i]) q.push(i);
    	while (q.size())
    	{
    		ll u=q.front();
    		q.pop();
    		for (ll i=head[u];~i;i=e[i].next)
    		{
    			ll v=e[i].to;
    			if (cnt[v]<deg[v])
    			{
    				cnt[v]++;
    				if (cnt[v]==deg[v])
    					q.push(v);
    			}
    		}
    	}
    	for (ll i=1;i<=n;i++)
    		if (cnt[i]<deg[i]) return 0;
    	return 1;
    }
    
    ll fpow(ll x,ll k)
    {
    	ll ans=1;
    	for (;k;k>>=1,x=x*x%MOD)
    		if (k&1) ans=ans*x%MOD;
    	return ans;
    }
    
    ll C(ll n,ll m)
    {
    	ll inv=fpow(fac[m]*fac[n-m]%MOD,MOD-2);
    	return fac[n]*inv%MOD;
    }
    
    void dfs(ll x)
    {
    	f[x]=1; size[x]=0;
    	for (ll i=head[x];~i;i=e[i].next)
    	{
    		ll v=e[i].to;
    		dfs(v);
    		size[x]+=size[v];
    		f[x]=C(size[x],size[v])*f[v]%MOD*f[x]%MOD;
    	}
    	size[x]++;
    }
    
    int main()
    {
    	freopen("photo.in","r",stdin);
    	freopen("photo.out","w",stdout);
    	scanf("%lld",&Q);
    	while (Q--)
    	{
    		scanf("%lld%lld%lld",&n,&m,&MOD);
    		prework();
    		for (ll i=1,x,y;i<=m;i++)
    		{
    			scanf("%lld%lld",&x,&y);
    			add(y,x);
    			deg[x]++;
    		}
    		if (!topsort()) 
    		{
    			printf("0\n");
    			continue;
    		}
    		ll s=0;
    		for (ll i=1;i<=n;i++)
    			if (!deg[i])
    			{
    				dfs(i);
    				s+=size[i];
    				ans=ans*f[i]*C(s,size[i])%MOD;
    			}
    		printf("%lld\n",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    软件需求分析阅读笔记1
    在Ubuntu上搭建Zookeeper集群
    第三周学习进度总结
    读书计划
    假期周进度报告3
    热词分析及解释
    假期周进度报告(二)
    假期周进度报告(一)
    PHP商城
    第十六周总结
  • 原文地址:https://www.cnblogs.com/stoorz/p/13679104.html
Copyright © 2011-2022 走看看