洛谷 P4099 [HEOI2013] SAO
设 \(f _ {x, i}\) 表示 \(x\) 节点在子树内的排名为 \(i\) 的方案数。类似树形背包,依次枚举 \(x\) 的所有子节点 \(y\),并把 \(y\) 加入 \(x\) 中:
对于 \(p _ x < p _ y\) 的情况,枚举 \(f _ {x, i}\) 与 \(f _ {y, j}\) 合并,但这样是不够的,还要再枚举一个 \(k\)(\(k < j\))表示 \(y\) 子树中有 \(k\) 个节点比 \(x\) 小。方案数即对于 \(x\) 前后分别插板,为 \(\displaystyle \binom {i - 1 + k} k \binom {s _ x - i + s _ y - k} {s _ y - k}\)。发现这与 \(j\) 无关,于是对 \(f _ {y, j}\) 做后缀和转移即可。
对于 \(p _ x > p _ y\) 的情况同上。时间复杂度 \(\text O (T n ^ 2)\)。
#include<cstdio>
#include<cstring>
#define N 1005
using namespace std;
const int mod=1e9+7;
int T,n,ans,siz[N],C[N][N],f[N][N],g[N][N],h[N][N];
int tot,head[N],nxt[N*2],ver[N*2],e[N*2];
inline void add(int &x,long long y) {x=(x+y)%mod;}
void init(int n) {
for(int i=0;i<=n;i++)
for(int j=C[i][0]=1;j<=i;j++)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
}
void insert(int x,int y,int z) {
ver[++tot]=y,e[tot]=z,nxt[tot]=head[x],head[x]=tot;
}
void dp(int x,int fa) {
siz[x]=1,f[x][1]=1;
for(int i=head[x],y;i;i=nxt[i]) {
if((y=ver[i])==fa) continue;
dp(y,x);
for(int j=siz[x];j;j--) {
int w=f[x][j]; f[x][j]=0;
if(e[i])
for(int k=0;k<siz[y];k++) {
add(f[x][j+k],1ll*w*C[j-1+k][k]%mod*C[siz[x]-j+siz[y]-k][siz[y]-k]%mod*h[y][k+1]);
}
else
for(int k=1;k<=siz[y];k++) {
add(f[x][j+k],1ll*w*C[j-1+k][k]%mod*C[siz[x]-j+siz[y]-k][siz[y]-k]%mod*g[y][k]);
}
}
siz[x]+=siz[y];
}
for(int i=1;i<=siz[x];i++)
g[x][i]=(g[x][i-1]+f[x][i])%mod;
for(int i=siz[x];i;i--)
h[x][i]=(h[x][i+1]+f[x][i])%mod;
}
int main() {
init(1000),scanf("%d",&T);
XJX:while(T--) {
tot=0;
for(int i=0;i<n;i++) head[i]=0;
scanf("%d",&n);
for(int i=1,x,y;i<n;i++) {
char c; scanf("%d %c %d",&x,&c,&y);
int z=c=='<'; insert(x,y,z),insert(y,x,!z);
}
memset(f,0,sizeof(f)),memset(g,0,sizeof(g)),memset(h,0,sizeof(h));
dp(0,-1),ans=0;
for(int i=1;i<=n;i++) add(ans,f[0][i]);
printf("%d\n",ans);
}
return 0;
}

浙公网安备 33010602011771号