[HEOI 2013] SAO

SAO…

题目大意:给定一棵树,每条边上有父子的大小关系,问有多少种满足所有关系的排列。

我们设 f [ i ] [ j ] f[i][j] f[i][j] 表示以 i i i 为根的子树中, i i i 的排名为 j j j 的方案数。然后我们考虑合并两棵子树。假设我们要从 f [ u ] [ i ] f[u][i] f[u][i] f [ v ] [ j ] f[v][j] f[v][j] 转移到 f [ u ] [ k ] f[u][k] f[u][k],假设 u < v u<v u<v,首先应该满足 k − i < j < = s i z e [ v ] k-i<j<=size[v ] ki<j<=size[v]。发现如果我们确定了原先 u u u 的排列放在哪些位置,那么就只能按顺序放进去。因此问题等价于给 u u u 的排列规定一些位置。那么显然, f [ u ] [ k ] + = f [ u ] [ i ] ⋅ f [ v ] [ j ] ⋅ ( k − 1 i − 1 ) ⋅ ( s i z e [ u ] + s i z e [ v ] − k s i z e [ u ] − i ) f[u][k]+=f[u][i]· f[v][j]·{k-1\choose i-1}·{size[u]+size[v]-k\choose size[u]-i} f[u][k]+=f[u][i]f[v][j](i1k1)(size[u]isize[u]+size[v]k)
于是我们可以写出转移方程:
f [ u ] [ k ] = ∑ i = 1 s i z e [ u ] ∑ j = k − i + 1 s i z e [ v ] f [ u ] [ i ] ⋅ f [ v ] [ j ] ⋅ ( k − 1 i − 1 ) ⋅ ( s i z e [ u ] + s i z e [ v ] − k s i z e [ u ] − i ) f[u][k]=\sum_{i=1}^{size[u]}\sum_{j=k-i+1}^{size[v]}f[u][i]·f[v] [j]·{k-1\choose i-1}·{size[u]+size[v]-k\choose size[u]-i} f[u][k]=i=1size[u]j=ki+1size[v]f[u][i]f[v][j](i1k1)(size[u]isize[u]+size[v]k)

把关于 j j j 的前缀和优化掉:

f [ u ] [ k ] = ∑ i = 1 s i z e [ u ] ( s u m [ s i z e [ v ] ] − s u m [ k − i ] ) ⋅ f [ u ] [ i ] ⋅ ( k − 1 i − 1 ) ⋅ ( s i z e [ u ] + s i z e [ v ] − k s i z e [ u ] − i ) f[u][k]=\sum_{i=1}^{size[u]}(sum[size[v]]-sum[k-i])·f[u][i]·{k-1\choose i-1}·{size[u]+size[v]-k\choose size[u]-i} f[u][k]=i=1size[u](sum[size[v]]sum[ki])f[u][i](i1k1)(size[u]isize[u]+size[v]k)

时间复杂度 O ( n 2 ) O(n^2) O(n2)

#include
#include
#include
#include
#include
#define ll unsigned long long
using namespace std;
struct edge{
    int to,next,w;
}ed[2010];
const int mod=1e9+7;
int head[1010],sz;
ll f[1010][1010],sum[1010][1010],c[1010][1010];
int size[1010];
inline void add_edge(int from,int to,int w)
{
    ed[++sz].to=to;
    ed[sz].next=head[from];
    ed[sz].w=w;
    head[from]=sz;
}
inline int read()
{
    char c=getchar();int x=0,flag=1;
    while(!isdigit(c)) flag*=c=='-'?-1:1,c=getchar();
    while(isdigit(c)) x=x*10+c-'0',c=getchar();
    return x*flag;
}
void init()
{
    memset(head,0,sizeof(head));
    sz=0;
    memset(f,0,sizeof(f));
    memset(sum,0,sizeof(sum));
}
void dfs(int u,int fff)
{
    size[u]=1;
    f[u][1]=1;
    for(int i=head[u];i;i=ed[i].next)
    {
        int v=ed[i].to;
        if(v==fff) continue;
        dfs(v,u);
        if(ed[i].w==0)
        {
            for(int k=size[u]+size[v];k>=1;k--)
            {
                ll ans=0;
                for(int l=1,lim1=min(size[u],k);l<=lim1;l++)
                {
                    ll tmp=sum[v][size[v]]-sum[v][k-l]+mod;
                    if(tmp>mod) tmp-=mod;
                    ll ss=f[u][l]*c[k-1][l-1]%mod*c[size[u]+size[v]-k][size[u]-l]%mod*tmp%mod;
                    ans=(ans+ss)%mod;
                }
                f[u][k]=ans;
            }
        }
        else
        {
            for(int k=size[u]+size[v];k>=1;k--)
            {
                ll ans=0;
                for(int l=1,lim1=min(size[u],k);l<=lim1;l++)
                {
                    ll tmp=sum[v][k-l];
                    ll ss=f[u][l]*c[k-1][l-1]%mod*c[size[u]+size[v]-k][size[u]-l]%mod*tmp%mod;
                    ans=(ans+ss)%mod;
                }
                f[u][k]=ans;
            }
        }
        size[u]+=size[v];
    }
    for(int i=1;i<=size[u];i++) sum[u][i]=(sum[u][i-1]+f[u][i])%mod;
}
int main()
{
    int T=read();
    c[0][0]=1;
    for(int i=1;i<=1000;i++)
    {
        c[i][0]=1;
        for(int j=1;j<=i;j++)
        {
            c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
        }
    }
    while(T--)
    {
        init();
        int n=read();
        for(int i=1;i');
            add_edge(v,u,s[0]=='<');
        }
        dfs(1,1);
        ll ans=0;
        for(int i=1;i<=n;i++) ans=(ans+f[1][i])%mod;
        cout<

你可能感兴趣的:([HEOI 2013] SAO)