P4365 [九省联考2018]秘密袭击coat(插值+线段树合并优化dp)
P4365 [九省联考2018]秘密袭击coat
先把问题转化为,对于每个权值
这是一个卷积的形式。把
对应位置相乘、相加,这是一个线段树合并的形式。每次进行的操作为:全局赋值为 1 ,
考虑一个变换,初始时
手推一下发现这个变换有结合律。
-
全局赋值为 1 :
*(0,1,0,0) -
[1,d_u]$ 乘 $x$ :$*(x,0,0,0) -
全局+1:
*(1,1,0,0) -
将
F 加到G 上:*(1,0,1,0)
用线段树维护这个变换即可。最后再用拉格朗日插值求出
#include<bits/stdc++.h>
#define ls t[ro].l
#define rs t[ro].r
#define mid ((l+r)>>1)
using namespace std;
typedef long long ll;
namespace FGF
{
int n,K,w;
const int N=2005,mo=64123;
struct Node{
ll a,b,c,d;
Node(){}
Node(int _a,int _b,int _c,int _d):a(_a),b(_b),c(_c),d(_d){};
};
Node operator *(Node x,Node y){return Node(x.a*y.a%mo,(y.a*x.b+y.b)%mo,(x.a*y.c+x.c)%mo,(y.c*x.b+x.d+y.d)%mo);}
struct tree{
int l,r;Node val;
void init(){l=r=0;val=Node(1,0,0,0);}
}t[N*40];
int a[N],rt[N],num,ans[N],inv[N];
vector<int> g[N];
void pushdown(int ro)
{
if(!ls)ls=++num,t[ls].init();
if(!rs)rs=++num,t[rs].init();
t[ls].val=t[ls].val*t[ro].val,t[rs].val=t[rs].val*t[ro].val;
t[ro].val=Node(1,0,0,0);
}
ll query(int ro,int l,int r)
{
if(l==r)return t[ro].val.d;
pushdown(ro);
return (query(ls,l,mid)+query(rs,mid+1,r))%mo;
}
void updat(int ro,int l,int r,int L,int R,const Node &x)
{
if(L<=l&&r<=R){t[ro].val=t[ro].val*x;return;}
pushdown(ro);
if(L<=mid)updat(ls,l,mid,L,R,x);
if(R>mid)updat(rs,mid+1,r,L,R,x);
}
int merge(int &x,int &y)
{
if(!x||!y)return x+y;
if(!t[x].l&&!t[x].r)swap(x,y);
if(!t[y].l&&!t[y].r)
{
t[x].val=t[x].val*Node(t[y].val.b,0,0,t[y].val.d);
return x;
}
pushdown(x),pushdown(y);
t[x].l=merge(t[x].l,t[y].l),t[x].r=merge(t[x].r,t[y].r);
return x;
}
void dfs(int u,int f,int k)
{
rt[u]=++num,t[rt[u]].init();
updat(rt[u],1,w,1,w,Node(0,1,0,0));
updat(rt[u],1,w,1,a[u],Node(k,0,0,0));
for(auto v:g[u])
if(v!=f)dfs(v,u,k),updat(rt[v],1,w,1,w,Node(1,1,0,0)),rt[u]=merge(rt[u],rt[v]);
updat(rt[u],1,w,1,w,Node(1,0,1,0));
}
int Lagrange()
{
static int f[N],inv[N],g[N],s;
inv[1]=f[0]=1;
for(int i=2;i<=n+1;i++)
inv[i]=1ll*inv[mo%i]*(mo-mo/i)%mo;
for(int i=1;i<=n+1;i++)
for(int j=n+1;j>=0;j--)
f[j]=((j?f[j-1]:0)+1ll*(mo-i)*f[j]%mo)%mo;
for(int i=1;i<=n+1;i++)
{
g[0]=1ll*f[0]*(mo-inv[i])%mo;
for(int j=1;j<=n+1;j++)
g[j]=1ll*(f[j]-g[j-1]+mo)%mo*(mo-inv[i])%mo;
int tmp=ans[i];
for(int j=1;j<=n+1;j++)
{
if(i>j)tmp=1ll*tmp*inv[i-j]%mo;
if(i<j)tmp=1ll*tmp*(mo-inv[j-i])%mo;
}
for(int j=K;j<=n+1;j++)
s=(s+1ll*tmp*g[j]%mo)%mo;
}
return s;
}
void work()
{
scanf("%d%d%d",&n,&K,&w);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1,u,v;i<n;i++)
scanf("%d%d",&u,&v),g[u].push_back(v),g[v].push_back(u);
for(int i=1;i<=n+1;i++)
{
num=0;
dfs(1,0,i);
ans[i]=query(rt[1],1,w);
}
printf("%d\n",Lagrange());
}
}
int main()
{
FGF::work();
return 0;
}