LGP8290题解
场外选手再次口胡
这种东西如果是链就很好做,并且有链的部分分。。。那么就照着这个方向想想吧。
考虑计算对于一个序列,全部都被填了数字的方案数。
容易发现枚举最大值有很简单的 \(O(nK)\) 做法:
\[\sum_{x=0}^{\infty}\prod_{i=1}^{n}(\min(x+K,r_i)-\max(x,l_i))-\prod_{i=1}^{n}(\min(x+K-1,r_i)-\max(x,l_i))
\]
两部分差的不多,所以只用考虑一边。
并且容易发现这个是点权的 \(0\) 次和,所以先暂且不考虑 \(1\) 次和,做法可以凭感觉确定是类似的(
容易发现,可以把 \(l_i\) 和 \(r_i\) 分成四类贡献。
然后,我们将所有的 \(l_i,r_i,l_i+K,r_i+K,l_i-K,r_i-K\) 全部拉出来排序,每个 \(x\) 能够对应排序后的一段。然后容易发现,一段对一个节点的贡献是固定的。
也就是说,如果 \(x\) 在离散化后的某个区间上,那么一个节点对其的贡献一定是一个和 \(x\) 有关的一次多项式。
于是我们枚举 \(x\) 在哪一段,问题就变成了先对每一段计算多项式,然后计算 一棵树上所有路径的多项式之积之和。
这种典中典问题当然是用点分治最好。。。但是模数不是 \(998244353\)。
考虑 NTT 的本质是插值。于是我们就插值。可以知道答案多项式一定是 \(O(n)\) 次的,于是代入 \(O(n)\) 跑一遍拉插就行了。
一棵树上所有路径的点权之积之和,这种典中典问题可以 DP \(O(n)\) 解决掉。
然后,求 \(\sum_{i=0}^{x}F(x)\) 这种东西,稍微推一下式子:
\[\sum_{i=0}^{x}\sum_{k=0}^{n}f_ki^k
\]
\[\sum_{k=0}^{n}f_k\sum_{i=0}^{x}i^k
\]
众所周知后面是和 \(k\) 有关的多项式,在最开始时预处理即可。
复杂度 \(O(n^3)\)。
下面是一个复杂度正确但是写了一整天而且常数巨大被卡飞的算法:
#include<algorithm>
#include<cstdio>
typedef unsigned ui;
const ui M=205,mod=1e9+7,inv2=500000004,Inv2=500000003;
ui n,K,cnt,h[M],l[M],r[M],dp1[M],dp2[M],val1[M],val2[M];bool vis[M];
ui len,lsh[M<<2];ui q[M<<1],p[M<<1];ui inv[M];ui ans1,ans2;ui lf[M],lg[M],f[M][3],g[M][3];ui ifac[M<<1];
struct Edge{
ui v,nx;
}e[M<<1];
inline void Add(const ui&u,const ui&v){
e[++cnt]=(Edge){v,h[u]};h[u]=cnt;
e[++cnt]=(Edge){u,h[v]};h[v]=cnt;
}
inline ui calc(ui*F,const ui&x){
return((1ull*F[2]*x+F[1])%mod*x+F[0])%mod;
}
namespace cal{
ui y[M<<1],q[M<<1],p[M<<1],val[M<<1];
inline ui calc(const ui&n,const ui&X){
ui ans(0);q[0]=p[n+1]=1;
for(ui i=1;i<=n;++i)val[i]=1ull*y[i]*ifac[i-1]%mod*(n-i&1?mod-ifac[n-i]:ifac[n-i])%mod;
for(ui i=1;i<=n;++i)q[i]=1ull*q[i-1]*(X+mod-i)%mod;for(ui i=n;i>=1;--i)p[i]=1ull*p[i+1]*(X+mod-i)%mod;
for(ui i=1;i<=n;++i)ans=(ans+1ull*q[i-1]*p[i+1]%mod*val[i])%mod;return ans;
}
}
inline void calc(const ui&u,const ui&fa,ui&ans1,ui&ans2){
dp1[u]=val1[u];dp2[u]=val2[u];ans1=(ans1+val1[u])%mod;ans2=(ans2+val2[u])%mod;
for(ui v,E=h[u];E;E=e[E].nx)if((v=e[E].v)^fa){
calc(v,u,ans1,ans2);if(!vis[v]||!vis[u])continue;
ans1=(ans1+1ull*dp1[u]*dp1[v])%mod;
ans2=(ans2+1ull*dp2[u]*dp1[v]+1ull*dp1[u]*dp2[v])%mod;
dp1[u]=(dp1[u]+1ull*val1[u]*dp1[v])%mod;
dp2[u]=(dp2[u]+1ull*val2[u]%mod*dp1[v]+1ull*val1[u]*dp2[v])%mod;
}
}
inline void Solve(const ui&K,const bool&typ,const ui&lim){
ui A(0),B(0);len=0;
for(ui i=1;i<=n;++i){
if(r[i]+K<=lim)lsh[++len]=r[i]+K;if(r[i]<=lim)lsh[++len]=r[i];
if(l[i]+K<=lim)lsh[++len]=l[i]+K;if(l[i]<=lim)lsh[++len]=l[i];
}
lsh[++len]=0;lsh[++len]=lim;
std::sort(lsh+1,lsh+len+1);len=std::unique(lsh+1,lsh+len+1)-lsh-1;
for(ui i=1;i<len;++i){
const ui&x=lsh[i+1];
for(ui u=1;u<=n;++u){
ui*F=f[u],*G=g[u];F[0]=F[1]=G[0]=G[1]=G[2]=0;
if(x<=l[u]||r[u]+K<x){
vis[u]=false;lf[u]=lg[u]=1;continue;
}
vis[u]=true;lf[u]=2;lg[u]=3;
if(x<=r[u]){
F[1]=(F[1]+1)%mod;
G[2]=(G[2]+inv2)%mod,G[1]=(G[1]+inv2)%mod;
}
else{
F[0]=(F[0]+r[u])%mod;
G[0]=(G[0]+1ull*r[u]*(r[u]+1)%mod*inv2)%mod;
}
if(x<=l[u]+K){
F[0]=(F[0]+mod-l[u])%mod;
G[0]=(G[0]+1ull*l[u]*(l[u]+1)%mod*Inv2)%mod;
}
else{
F[1]=(F[1]+mod-1)%mod,F[0]=(F[0]+K)%mod;
G[2]=(G[2]+Inv2)%mod,G[1]=(G[1]+1ull*inv2*(2*K-1))%mod,G[0]=(G[0]+1ull*K*(K-1)%mod*Inv2)%mod;
}
}
const ui&L=lsh[i],&R=lsh[i+1];ui m1(1),m2(1),qa,pb;
for(ui i=1;i<=n;++i)m1+=lf[i]-1,m2+=lg[i]-1;
for(ui i=1;i<=m2+1;++i){
for(ui u=1;u<=n;++u)val1[u]=vis[u]?calc(f[u],i):0,val2[u]=vis[u]?calc(g[u],i):0;
qa=pb=0;calc(1,0,qa,pb);q[i]=qa;p[i]=pb;
}
for(ui i=1;i<=m1+1;++i)cal::y[i]=(cal::y[i-1]+q[i])%mod;A=(A+cal::calc(m1+1,R)+mod-cal::calc(m1+1,L))%mod;
for(ui i=1;i<=m2+1;++i)cal::y[i]=(cal::y[i-1]+p[i])%mod;B=(B+cal::calc(m2+1,R)+mod-cal::calc(m2+1,L))%mod;
}
if(!typ)ans1=A,ans2=B;else ans1=(ans1+mod-A)%mod,ans2=(ans2+mod-B)%mod;
}
signed main(){
scanf("%u%u",&n,&K);
ifac[0]=ifac[1]=1;
for(ui i=2;i<=(n*2+5);++i)ifac[i]=1ull*(mod-mod/i)*ifac[mod%i]%mod;
for(ui i=1;i<=(n*2+5);++i)ifac[i]=1ull*ifac[i-1]*ifac[i]%mod;
for(ui i=1;i<=n;++i)scanf("%u%u",l+i,r+i),--l[i];for(ui u,v,i=1;i<n;++i)scanf("%u%u",&u,&v),Add(u,v);
Solve(K+1,0,1e9);Solve(K,1,1e9-1);printf("%u\n%u",ans1,ans2);
}