BZOJ 3167 [Heoi2013]Sao ——树形DP
唔,好题。
拓扑序的计数问题。
忽略边的方向,得到一棵树。
然后我们用$f[i][j]$表示节点$i$在子树中排第$j$位的方案数。
然后直接DP,可以得到$n^3$的算法,然后再用前缀和优化一下就是$n^2$可以过掉本题。
是BZOJ4824的强化版
#include <map> #include <cmath> #include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define F(i,j,k) for (ll i=j;i<=k;++i) #define D(i,j,k) for (ll i=j;i>=k;--i) #define ll long long #define mp make_pair #define maxn 1050 const ll md=1000000007; ll h[maxn<<1],to[maxn<<1],ne[maxn<<1],en=0,n; ll siz[maxn],tmp[maxn],w[maxn],lim[maxn]; ll dp[maxn][maxn],c[maxn][maxn],suf[maxn][maxn],pre[maxn][maxn]; char s[maxn]; void add(ll a,ll b,ll c) { to[en]=b;ne[en]=h[a];w[en]=c;h[a]=en++; } ll C(ll n,ll m) { if (n<0||m<0) return 0; return c[n][m]; } void Tree_DP(ll o,ll fa) { dp[o][1]=1;siz[o]=1; for (ll i=h[o];i>=0;i=ne[i]) if (to[i]!=fa){ Tree_DP(to[i],o); F(j,0,siz[o]+siz[to[i]]+2) tmp[j]=0; if (w[i]==1) { F(j,1,siz[o]) if (dp[o][j]) F(_j,j,j+siz[to[i]]-1) tmp[_j]+=(((dp[o][j]*suf[to[i]][_j-j+1])%md*C(_j-1,j-1))%md*C(siz[o]+siz[to[i]]-_j,siz[o]-j))%md,tmp[_j]%=md; } else { F(j,1,siz[o]) if (dp[o][j]) F(_j,j+1,siz[to[i]]+j) tmp[_j]+=(((dp[o][j]*((suf[to[i]][1]-suf[to[i]][_j-j+1])%md+md))%md*C(_j-1,j-1))%md*C(siz[o]+siz[to[i]]-_j,siz[o]-j))%md,tmp[_j]%=md; } siz[o]+=siz[to[i]]; F(j,0,siz[o]) dp[o][j]=(tmp[j]%md+md)%md; } suf[o][siz[o]]=dp[o][siz[o]]; D(i,siz[o],1) suf[o][i]=(suf[o][i+1]+dp[o][i])%md; } void Finout() { freopen("in.txt","r",stdin); freopen("wa.txt","w",stdout); // freopen("keyboard.in","r",stdin); // freopen("keyboard.out","w",stdout); } ll t; int main() { scanf("%lld",&t); c[1][0]=1;c[1][1]=1;c[0][0]=1; F(i,2,maxn-1) { c[i][0]=1; F(j,1,maxn-1) c[i][j]=(c[i-1][j]+c[i-1][j-1])%md; } while (t--) { memset(dp,0,sizeof dp); memset(suf,0,sizeof suf); memset(h,-1,sizeof h);en=0; scanf("%lld",&n); F(i,2,n) { ll a,b,c; scanf("%lld",&a); a++; scanf("%s",s); scanf("%lld",&b);b++; switch(s[0]) { case '<':c=1;break; case '>':c=-1;break; } add(a,b,c); add(b,a,-c); } Tree_DP(1,-1); ll ans=0; F(i,0,n) ans+=dp[1][i],ans%=md; printf("%lld\n",(ans%md+md)%md); } }