• 联考20200803 T1 欢迎来到塞莱斯特山





    分析:
    树上排列计数不止一次遇到了,我们依然考虑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\)中的\(t\)段和\(j\)中的\(t\)段合并(可能是\(i\)在前面,也可能是\(j\)在前面,一共两种情况)
    或者\(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]);
    }
    

  • 相关阅读:
    进程-线程-消息队列
    用Ogre实现《天龙八部》场景中水面(TerrainLiquid)详解
    TCP协议三次握手过程分析【图解,简单清晰】
    excel批量删除sql语句
    批量删除指定盘指定格式文件
    Linux命令速查手册(第2版)学习
    List、Map、Set 三个接口,存取元素时,各有什么特点
    HashMap 什么时候进行扩容呢
    两个对象值相同 (x.equals(y) == true),但却可有不同的 hash code,这句话对不对?
    ArrayList,Vector, LinkedList 的存储性能和特性
  • 原文地址:https://www.cnblogs.com/IzayoiDoyo/p/13428142.html
Copyright © 2020-2023  润新知