传送门
考虑给的这张图是n个点以及n条边,并且满足拓扑序,也就是说,假设我们的边不是有向边的话,那么我们的这个图将形成一颗树.这个性质很重要,所以我们考虑从树形结构下手.
根据树形dp的一半套路,考虑使用 d p [ u ] dp[u] dp[u]来表示以 u u u为根节点的子树的拓扑序的个数.那么此时我们需要考虑的就是所有儿子 v v v的合并问题了(此处的合并类似与树形背包).
需要注意的是,此时我们用遍历树的方法来遍历树形图,所以u,v的边的方向我们是需要考虑的,为了讨论方便起见,此时我们先假定u需要在v前面
然后我们发现光光记录上述的个数显然是无法将 u u u和 v v v合并的.因为我们根本不知道每一种拓扑序的样子,光光记录个数无从下手.
考虑将 d p dp dp方程进一步扩展,使用 d p [ u ] [ i ] dp[u][i] dp[u][i]来表示以 u u u为根的子树并且u在拓扑序中第 i i i位的种类数.显然 u u u所有的拓扑序个数就是所有的 d p [ u ] [ i ] dp[u][i] dp[u][i]的和
诶,此时我们似乎可以进行合并了.
对于我们现在的 d p [ u ] [ i ] dp[u][i] dp[u][i]和 u u u的一个子树 v , d p [ v ] [ j ] v,dp[v][j] v,dp[v][j],我们考虑如何将其合并.显然我们可以枚举合并后 u u u在拓扑序中的位置,设合并后 u u u在第 k k k位.那么对于此次合并来说,本来在 u u u前面的点现在肯定还是在 u u u前面的,那么我们多出来的点就是原来在 v v v前面的了(注意此时我们假定u在v前面,那么v后面的肯定不可能在u前面).并且因为u,v序列各自的相对位置是不能发生改变的,我们可以使用这样的想法来统计:我们考虑将u序列的每一个数放在合并后的序列的哪一个位置.因为假设我们将u放下了,相对的v也被放下了.那么此时不难推出合并带来的贡献就是: C k − 1 i − 1 ∗ C S i z e [ u ] + S i z e [ v ] − k S i z e [ u ] − i C_{k-1}^{i-1}*C_{Size[u]+Size[v]-k}^{Size[u]-i} Ck−1i−1∗CSize[u]+Size[v]−kSize[u]−i,那么合并的总式子就是 d p [ u ] [ k ] + = d p [ u ] [ i ] ∗ d p [ v ] [ j ] ∗ C k − 1 i − 1 ∗ C S i z e [ u ] + S i z e [ v ] − k S i z e [ u ] − i dp[u][k]+=dp[u][i]*dp[v][j]*C_{k-1}^{i-1}*C_{Size[u]+Size[v]-k}^{Size[u]-i} dp[u][k]+=dp[u][i]∗dp[v][j]∗Ck−1i−1∗CSize[u]+Size[v]−kSize[u]−i
此时不难发现上述式子的朴素求法是 n 3 n^3 n3的(考虑枚举所有的 i , j , k i,j,k i,j,k).所以需要优化一下求法.
然后我们会发现上述式子其实我们可以求出所有的前缀 d p [ v ] [ j ] dp[v][j] dp[v][j],我们将原式子使用分配率搞一下,就会发现可以化解为 d p [ u ] [ k ] + = ∑ j = 1 S i z e [ v ] d p [ v ] [ j ] ∗ d p [ u ] [ i ] ∗ C k − 1 i − 1 ∗ C S i z e [ u ] + S i z e [ v ] − k S i z e [ u ] − i dp[u][k]+=\sum_{j=1}^{Size[v]}dp[v][j]*dp[u][i]*C_{k-1}^{i-1}*C_{Size[u]+Size[v]-k}^{Size[u]-i} dp[u][k]+=j=1∑Size[v]dp[v][j]∗dp[u][i]∗Ck−1i−1∗CSize[u]+Size[v]−kSize[u]−i.用这种方法来省去一层循环.所以此时我们还需要做的就是如何通过 i , k i,k i,k的范围来推出 j j j的枚举范围.手模一下,不难发现我们的 k k k存在一种范围 i ≤ k ≤ i + j − 1 i \leq k \leq i+j-1 i≤k≤i+j−1,然后我们考虑反推出 j ≥ k − i + 1 j \geq k-i+1 j≥k−i+1,并且此时的 k k k的枚举范围是 [ i , S i z e [ u ] + S i z e [ v ] − 1 ] [i,Size[u]+Size[v]-1] [i,Size[u]+Size[v]−1].
现在考虑如果是 u u u在 v v v后面的情况,分析一下发现 d p dp dp方程应该和上述是基本一样的.就是枚举范围有一点变化,此时我们的 k k k的关系式是 k ≥ i + j , k ≤ i + S i z e [ v ] k \geq i+j,k \leq i+Size[v] k≥i+j,k≤i+Size[v],那么此时 i ∈ [ 1 , S i z e [ u ] ] , j ∈ [ 1 , k − i ] , k ∈ [ i + 1 , S i z e [ u ] + S i z e [ v ] ] i\in[1,Size[u]],j\in[1,k-i],k\in[i+1,Size[u]+Size[v]] i∈[1,Size[u]],j∈[1,k−i],k∈[i+1,Size[u]+Size[v]]
至此,本题结束
下面是具体的代码部分:
#include
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
ll x=0,w=1;char ch=getchar();
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*w;
}
inline void print(__int128 x){
if(x<0) {putchar('-');x=-x;}
if(x>9) print(x/10);
putchar(x%10+'0');
}
#define maxn 1000000
#define int long long
const int mod=1e9+7;
const double eps=1e-8;
#define int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
vector<int>edge[1010];int mp[1010][1010];
int dp[1010][1010],sum[1010][1010];int Size[maxn];int temp[1010];int C[1010][1010];
void dfs(int u,int per_u) {
Size[u]=1;dp[u][1]=1;
for(auto v:edge[u]) {
if(v==per_u) continue;
dfs(v,u);
memcpy(temp,dp[u],sizeof dp[u]);
memset(dp[u],0,sizeof dp[u]);
if(mp[u][v]==0) {
for(int i=1;i<=Size[u];i++) {
for(int j=i;j<=i+Size[v]-1;j++) {
dp[u][j]=(dp[u][j]+temp[i]*C[j-1][i-1]%mod*C[Size[u]+Size[v]-j][Size[u]-i]%mod\
*(sum[v][Size[v]]-sum[v][j+1-i-1]+mod)%mod)%mod;
}
}
}
else {
for(int i=1;i<=Size[u];i++) {
for(int j=i+1;j<=i+Size[v];j++) {
dp[u][j]=(dp[u][j]+temp[i]*C[j-1][i-1]%mod*C[Size[u]+Size[v]-j][Size[u]-i]%mod\
*sum[v][j-i]%mod)%mod;
}
}
}
Size[u]+=Size[v];
}
for(int i=1;i<=Size[u];i++) {
sum[u][i]=(sum[u][i-1]+dp[u][i])%mod;
}
}
void init() {
memset(dp,0,sizeof dp);memset(sum,0,sizeof sum);memset(Size,0,sizeof Size);
memset(temp,0,sizeof temp);
}
signed main() {
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-1]+C[i-1][j])%mod;
}
}
int T=read();
while(T--) {
init();
int n=read();
for(int i=1;i<=n;i++) edge[i].clear();
for(int i=1;i<=n-1;i++) {
int u=read();char c;cin>>c;int v=read();u++;v++;
if(c=='<') mp[u][v]=0,mp[v][u]=1;
else mp[u][v]=1,mp[v][u]=0;
edge[u].push_back(v);
edge[v].push_back(u);
}
dfs(1,0);
cout<<sum[1][Size[1]]<<endl;
}
return 0;
}