题目
DP求出完美集合的价值之和,这种树上联通块的DP一般可以用DFS序DP,把DFS序拿出来从右到左,如果当前dfs序为i的点选,则可以选择dfs序为i+1的点,否则跳过整颗子树,即
之类。
求树上的存在一种合法选点方案的方案数,发现合法选点方案构成一个联通块(或空集),那么如果一个联通块的贡献为1(有合法选点方案只算一种),可以算:所有点是合法选点的方案 - 所有边(所连接的两个点同时)是合法选点的方案。因为联通块点比边多一,只会算一遍,part1的算法可以很方便的改成统计(最大价值之和的)方案数与两个点都必须选并统计方案数。
得到集合的方案数后,还需要求选出K个集合的方案数即
集合的方案数简单估计是
即
又有
计算组合数不能用常规方法算。
发现模数的末尾为
,不是质数还超过了
,马上
,键入
,一试发现
组合数取模可以用多项式方法。
对于
可以用:
对
在
,
…等多处多点求值即可做到
的优秀复杂度(和巨大常数)。
对于
可以维护
中非5的倍数的乘积 + 因子5的个数然后合并即可。
非5倍数的乘积可用多项式
的常数项得到。
因为
其中后面的
的计算在
的意义下可以只计算前23项,剩余的都是0,又因为我们的答案只需要常数项,所以我们只需要保存多项式的前23项,暴力卷积就可以了,对于不是10的倍数的
把不是
的倍数的部分的最多9个一次式乘起来就行了。
计算因子5的个数可以
AC Code(还是有点长):
#include<bits/stdc++.h>
#define maxn 65
#define maxm 10005
#define LL long long
#define mod 11920928955078125ll
#define pll pair<LL,LL>
using namespace std;
int n,m,w[maxn],v[maxn];
int info[maxn],Prev[maxn<<1],to[maxn<<1],cst[maxn<<1],cnt_e;
void Node(int u,int v,int c){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v,cst[cnt_e]=c; }
inline LL mul(LL a,LL b,LL p=mod){
return ((a*b-(LL)((long double)a*b/p+1e-10)*p)+p)%p;
}
inline LL Pow(LL base,LL k){
LL ret=1;
for(;k;k>>=1,base=mul(base,base))
if(k&1)
ret=mul(ret,base);
return ret;
}
namespace Binom{
#define S 23
LL C[S][S],pw[S]={1},K;
struct poly{
LL a[S];
poly(LL A=0,LL B=0){memset(a,0,sizeof a);a[0]=A,a[1]=B;}
poly operator *(const poly &B)const{
poly ret;
for(int i=0;i<S;i++) if(a[i])
for(int j=0;j+i<S;j++) if(B.a[j])
ret.a[i+j] = (ret.a[i+j] + mul(a[i],B.a[j])) % mod;
return ret;
}
void init(LL k){
for(int i=1;i<S;i++) pw[i] = mul(pw[i-1],k);
static LL ar[S];
memset(ar,0,sizeof ar);
for(int i=0;i<S;i++)
for(int j=0;j<=i;j++)
ar[j] = (ar[j] + mul(a[i] , mul(pw[i-j],C[i][j]))) % mod;
memcpy(a,ar,sizeof ar);
}
}P[10005];
void Init(){
for(int i=0;i<S;i++){
C[i][0] = 1;
for(int j=1;j<=i;j++)
C[i][j] = (C[i-1][j-1] + C[i-1][j]) % mod;
}
P[0] = poly(1,0);
for(int i=1;i<=10000;i++){
if(i % 5) P[i] = P[i-1] * poly(i,1);
else P[i] = P[i-1];
}
}
poly facpoly(LL n){
if(n<=10000) return P[n];
LL k = n/10*10;
poly t1 = facpoly(k>>1) , t2 = t1;
t2.init(k>>1);
t1 = t1 * t2;
for(LL i=k+1;i<=n;i++)
if(i%5) t1 = t1 * poly(i,1);
return t1;
}
pll fac(LL n){
pll ret = make_pair(facpoly(n).a[0] , n/5);
if(n >= 5){
pll tmp = fac(n/5);
ret.first = mul(ret.first , tmp.first);
ret.second = ret.second + tmp.second;
}
return ret;
}
LL Combk(LL n){
if(n < K) return 0;
pll fk = fac(K),fn = fac(n) , fnk = fac(n-K);
fn.second -= fk.second + fnk.second;
fn.first = mul(mul(fn.first , Pow(mul(fk.first,fnk.first),mod/5*4-1)),Pow(5,fn.second));
return fn.first;
}
}
int dis[maxn][maxn],fa[maxn],can[maxn];
namespace DP{
LL f[maxn][maxm] , g[maxn][maxm] , can[maxn] , Max , Up;
int dfn[maxn] , cnt , siz[maxn];
void dfs(int now,int ff){
siz[now] = 1,dfn[++cnt] = now;
for(int i=info[now];i;i=Prev[i])
if(to[i]!=ff && can[to[i]])
dfs(to[i],now),siz[now] += siz[to[i]];
}
pll ser(int x,int y){
cnt=0;
dfs(x,0);
for(int i=0;i<=m;i++) f[cnt+1][i] = 0 , g[cnt+1][i] = 1;
for(int i=cnt;i>=1;i--){
int u = dfn[i];
for(int j=0;j<=m;j++){
if(u == y && j<w[u]) f[i][j] = g[i][j] = 0;
else if(u == y || (j>=w[u] && f[i+1][j-w[u]]+v[u] > f[i+siz[u]][j])){
f[i][j] = f[i+1][j-w[u]] + v[u];
g[i][j] = g[i+1][j-w[u]];
}
else if(j<w[u] || f[i+1][j-w[u]]+v[u] < f[i+siz[u]][j]){
f[i][j] = f[i+siz[u]][j];
g[i][j] = g[i+siz[u]][j];
}
else{
f[i][j] = f[i+siz[u]][j];
g[i][j] = g[i+siz[u]][j] + g[i+1][j-w[u]];
}
}}
return make_pair(f[1][m],g[1][m]);
}
LL solve(int x,int y){
for(int i=1;i<=n;i++)
can[i] = ((LL)dis[x][i] * v[i] <= Max && (LL)dis[y][i] * v[i] <= Max);
if(!can[x] || (y && !can[y])) return 0;
pll ret = ser(x,y);
if(ret.first == Up)
return Binom::Combk(ret.second);
return 0;
}
}
void dfs(int now,int ff,int pfa,int dist){
dis[pfa][now] = dist , fa[now] = ff;
for(int i=info[now];i;i=Prev[i])
if(to[i]!=ff)
dfs(to[i],now,pfa,dist+cst[i]);
}
int main(){
scanf("%d%d%lld%lld",&n,&m,&Binom::K,&DP::Max);
Binom::Init();
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
for(int i=1;i<=n;i++) scanf("%d",&v[i]);
for(int i=1;i<n;i++){
int u,v,w;scanf("%d%d%d",&u,&v,&w);
Node(u,v,w),Node(v,u,w);
}
for(int i=1;i<=n;i++) DP::can[i] = 1;
for(int i=1;i<=n;i++) DP::Up = max(DP::Up , DP::ser(i,0).first);
if(DP::Up == 0) return puts("0"),0;
for(int i=n;i>=1;i--) dfs(i,0,i,0);
LL ans = 0;
for(int i=1;i<=n;i++)
ans = (ans + DP::solve(i,0) - (fa[i] ? DP::solve(i,fa[i]) : 0)) % mod;
printf("%lld\n",(ans+mod)%mod);
}