聯考20200803 T1 歡迎來到塞萊斯特山
阿新 • • 發佈:2020-08-03
分析:
樹上排列計數不止一次遇到了,我們依然考慮DP
設\(f_{i,j}\)表示以\(i\)為根的子樹下面的點,在排列中形成了\(j\)的連續段,這\(j\)個連續段的相對位置確定的貢獻總和
合併依然是樹上揹包的方式合併,列舉已合併的部分\(i\)段,將要合併的子樹\(j\)段,得到\(k\)段,所合併的根\(u\)便是合併位置的LCA
這裡的複雜度時\(O(n^3)\)
現在的問題是\(i\)段和\(j\)段合併成\(k\)段的方案數
設\(g_{i,j,k}\)為\(i\)段和\(j\)段合併成\(k\)段的方案數
我們嘗試從\(k-1\)推到\(k\)
列舉新的一段由\(i\)
或者\(i\)中的\(t\)段和\(j\)中的\(t+1\)段合併
或者\(i\)中的\(t+1\)段和\(j\)中的\(t\)段合併
列出式子:
\[g_{i,j,k}=\sum_{t} 2g_{i-t,j-t,k-1}+g_{i-t-1,j-t,k-1}+g_{i-t,j-t-1,k-1} \]
發現列舉\(t\)時\(i-j\)始終不變,我們可以把值存在\(sum_{i-j}\)中優化一下
這裡的複雜度也是\(O(n^3)\)
總複雜度\(O(n^3)\),然而我的跑很慢。。。
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #define maxn 505 #define MOD 1000000007 using namespace std; inline int getint() { int num=0,flag=1;char c; while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1; while(c>='0'&&c<='9')num=num*10+c-48,c=getchar(); return num*flag; } int n; int fir[maxn],nxt[maxn],to[maxn],cnt; int f[maxn][maxn],g[maxn][maxn][maxn],pw[maxn][maxn],sum[maxn<<1],tmp[maxn]; int sz[maxn]; inline void newnode(int u,int v) {to[++cnt]=v,nxt[cnt]=fir[u],fir[u]=cnt;} inline int upd(int x){return x<MOD?x:x-MOD;} inline void dfs(int u,int dpt) { sz[u]=1;f[u][1]=1; for(int i=fir[u];i;i=nxt[i]) { int v=to[i];dfs(v,dpt+1); for(int j=1;j<=sz[u]+sz[v];j++)tmp[j]=0; for(int j=1;j<=sz[u];j++)for(int k=1;k<=sz[v];k++) for(int t=max(1,j-k);t<=j+k;t++)tmp[t]=(tmp[t]+1ll*g[j][k][t]*f[u][j]%MOD*f[v][k]%MOD*pw[dpt][j+k-t])%MOD; sz[u]+=sz[v]; for(int j=1;j<=sz[u];j++)f[u][j]=tmp[j]; } } int main() { n=getint(); for(int i=2;i<=n;i++)newnode(getint(),i); for(int i=1;i<=n;i++) { pw[i][0]=1; for(int j=1;j<=n;j++)pw[i][j]=1ll*pw[i][j-1]*i%MOD; } g[0][0][0]=1; for(int k=1;k<=n;k++) { memset(sum,0,sizeof sum); for(int i=0;i<=n;i++)for(int j=0;i+j<=n;j++) { if(i+j>=k)g[i][j][k]=(2ll*sum[i-j+n]+sum[i-j+n-1]+sum[i-j+n+1])%MOD; sum[i-j+n]=upd(sum[i-j+n]+g[i][j][k-1]); } } dfs(1,1); printf("%d\n",f[1][1]); }