题解:P16100 [ICPC 2019 NAIPC] Heaps of Fun
lailai0916 · · 题解
题意简述
树上每个点
解题思路
先计算满足大小关系的取值区域体积,再除以整个取值区域的体积
设
其中
这些函数不是全局多项式:例如叶子函数会在
将所有
在当前区间中改用变量
这是将
若
每个区间按树的后序遍历计算。先卷积所有孩子的系数,设乘积为
将得到的多项式代入
代码中,f[u][i] 保存当前区间的多项式系数,val[u] 保存上一区间传来的 ord 保存后序顺序,确保合并时孩子系数已经属于当前区间;g 和 tmp 依次合并孩子多项式。求值采用从高次项到低次项的乘加形式,避免重复计算幂。
所有系数都可以直接在模数下计算。积分产生的除数至多为
每个区间的多项式合并总计
参考代码
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=305;
const int mod=1000000007;
int cnt;
int a[N],b[N],ord[N],siz[N],f[N][N],g[N],tmp[N],val[N],inv[N];
vector<int> G[N];
ll Pow(ll x,ll y)
{
x%=mod;
ll res=1;
while(y)
{
if(y&1)res=res*x%mod;
x=x*x%mod;
y>>=1;
}
return res;
}
void dfs(int u)
{
siz[u]=1;
for(auto v:G[u]){dfs(v);siz[u]+=siz[v];}
cnt++;
ord[cnt]=u;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin>>n;
int rt=0;
ll ans=1;
for(int i=1;i<=n;i++)
{
int p;
cin>>b[i]>>p;
ans=ans*b[i]%mod;
a[i]=b[i];
if(p)G[p].push_back(i);
else rt=i;
}
dfs(rt);
for(int i=1;i<=n;i++)inv[i]=i==1?1:1ll*(mod-mod/i)*inv[mod%i]%mod;
sort(a+1,a+n+1);
for(int i=n;i>=1;i--)
{
if(a[i]==a[i-1])continue;
int h=a[i]-a[i-1];
for(int j=1;j<=n;j++)
{
int u=ord[j];
fill(f[u],f[u]+siz[u]+1,0);
if(b[u]<a[i]){val[u]=0;continue;}
g[0]=1;
int len=0;
for(auto v:G[u])
{
fill(tmp,tmp+len+siz[v]+1,0);
for(int j=0;j<=len;j++)
{
for(int k=0;k<=siz[v];k++)tmp[j+k]=(tmp[j+k]+1ll*g[j]*f[v][k])%mod;
}
len+=siz[v];
copy(tmp,tmp+len+1,g);
}
f[u][0]=val[u];
for(int j=0;j<=len;j++)f[u][j+1]=1ll*g[j]*inv[j+1]%mod;
val[u]=0;
for(int j=siz[u];j>=0;j--)val[u]=(1ll*val[u]*h+f[u][j])%mod;
}
}
cout<<val[rt]*Pow(ans,mod-2)%mod<<'\n';
return 0;
}