CF995F Cowmpany Cowmpensation
Solution
这道题目可以看出我的代码能力是有多渣(代码能力严重退化)
我们先考虑dp,很容易写出方程:
设(f_{i,j})表示以(i)为根的子树中(i)的值为(j),那么转移为:
[egin{aligned}
f_{i,j}=prod_{vin son_u}sum_{k=1}^j{f_{v,j}}
end{aligned}
]
这个东西很明显可以前缀和优化变成(O(n^2))的求解.
当然不会告诉你我dp写挂了然后身败名裂啊
发现进一步的优化.
这个东西如果全用前缀和搞起来不就很像一个函数了?(把每一项出现的拆开考虑)
emmm,好像是的.
那么显然这个东西可以通过点值确定这个函数,然后就是喜闻乐见的拉格朗日插值了.
但是为什么可以成为一个可确定性的函数呢(就是复杂度比较合适).
考虑叶子节点如果有的话肯定是一次函数.
emmm,如果深度增加,显然就会高一次.
深度最大是(n),所以应该只要确定(n)个点就可以了.
那么就很愉快的写完了.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<iostream>
using namespace std;
#define ll long long
#define re register
#define file(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout)
#define int ll
inline int gi()
{
int f=1,sum=0;char ch=getchar();
while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
return f*sum;
}
const int N=3010,Mod=1e9+7;
int dp[N][N],front[N],to[N<<1],nxt[N<<1],cnt,sum[N][N],n,x[N],y[N];
int Pow(int a,int b)
{
int ret=1;
while(b)
{
if(b&1)ret=(ret*a)%Mod;
a=(a*a)%Mod;b>>=1;
}
return ret;
}
int lalr(int k)
{
int ans=0;
for(int i=1;i<=n;i++)
{
int Up=1,Down=1;
for(int j=0;j<=n;j++)
if(i!=j)
{
(Up*=(k-x[j]))%=Mod;
(Down*=(x[i]-x[j]))%=Mod;
}
(ans+=(y[i]*Up)%Mod*Pow(Down,Mod-2))%=Mod;
}
return ans;
}
void Add(int u,int v)
{
to[++cnt]=v;nxt[cnt]=front[u];front[u]=cnt;
}
void dfs(int u)
{
for(int i=1;i<=n;i++)dp[u][i]=1;
for(int i=front[u];i;i=nxt[i])
{
int v=to[i];
dfs(v);
for(int j=1;j<=n;j++)
dp[u][j]=(ll)dp[u][j]*dp[v][j]%Mod;
}
for(int i=1;i<=n;i++)
dp[u][i]=(dp[u][i]+dp[u][i-1])%Mod;
}
void init()
{
dfs(1);
}
signed main()
{
int d;
n=gi();d=gi();
for(int i=2;i<=n;i++)
{
int Fa=gi();
Add(Fa,i);
}
init();
if(d<=n)return printf("%lld
",dp[1][d]),0;
for(int i=1;i<=n;i++)x[i]=i,y[i]=dp[1][i];
printf("%lld
",lalr(d));
return 0;
}