题目:https://www.luogu.org/problemnew/show/P4099
结果还是看了题解才会……
关键是状态,f[ i ][ j ] 表示 i 子树、 i 号点是第 j 个出现的方案数。
合并的时候,很重要的是去枚举孩子 v 有 k 个点放在了第 i 个点前面。这样 v 可以在的位置就根据该边是 > 还是 < 而是一个前/后缀。这样就是 n2 的了。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; int rdn() { int ret=0;bool fx=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return fx?ret:-ret; } int Mn(int a,int b){return a<b?a:b;} const int N=1005,mod=1e9+7; int upt(int x){while(x>=mod)x-=mod;while(x<0)x+=mod;return x;} int n,siz[N],c[N][N],f[N][N],pr[N][N],sc[N][N]; int hd[N],xnt,to[N<<1],nxt[N<<1];bool lx[N<<1];//1:< char ch; void add(int x,int y,bool fx) { to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;lx[xnt]=fx;} void dfs(int cr,int fa) { siz[cr]=1; f[cr][1]=1;// for(int i=hd[cr],v;i;i=nxt[i]) if((v=to[i])!=fa) { dfs(v,cr); siz[cr]+=siz[v]; for(int j=siz[cr];j;j--) { int lj=0; for(int k=0,l2=Mn(j-1,siz[v]);k<=l2;k++) { int tp=0; if(lx[i]) tp=(ll)f[cr][j-k]*sc[v][k+1]%mod; else tp=(ll)f[cr][j-k]*pr[v][k]%mod; tp=(ll)tp*c[j-1][k]%mod*c[siz[cr]-j][siz[v]-k]%mod; lj=upt(lj+tp); } f[cr][j]=lj; } } pr[cr][0]=sc[cr][siz[cr]+1]=0;/// for(int j=1;j<=siz[cr];j++) { pr[cr][j]=upt(pr[cr][j-1]+f[cr][j]); } for(int j=siz[cr];j;j--) { sc[cr][j]=upt(sc[cr][j+1]+f[cr][j]); } pr[cr][0]=sc[cr][siz[cr]+1]=0; } int main() { int T=rdn(),lm=1000; for(int i=0;i<=lm;i++)c[i][0]=1; for(int i=1;i<=lm;i++) for(int j=1;j<=i;j++) c[i][j]=upt(c[i-1][j]+c[i-1][j-1]); while(T--) { n=rdn(); memset(hd,0,sizeof hd); xnt=0; for(int i=1,u,v;i<n;i++) { u=rdn()+1;cin>>ch;v=rdn()+1; add(u,v,ch=='<'); add(v,u,ch=='>'); } dfs(1,0); printf("%d ",sc[1][1]); } return 0; }