完美的集合
题解
生平最讨厌的卡常。
其实这道题蛮好想的。
由题可知,如果一个集合能用,它一定是一个联通块而且总重量小于m。很明显的dp了,数据范围也不大。
我们可以先以每个节点为根,都做一次dp。
不过我们马上发现,时间复杂度是,明显要超时。那我们应该怎么做呢?
我们可以采取dfs序dp。因为我们只需求出每个根的dp值,那么其它点的dp值我们都是可以不管的,也就是它是什么值都无所谓。那么我们就将每个点的子树大小记录下来,对于一个点,如果它的dfs序为i,那么到属于它的子树。而它的dfs序减去它父亲下一个儿子的子树大小就是他父亲下一个儿子的dfs序。如此我们就可以将上一个儿子的dp值与下一个儿子的dp值放在一起,一直更新到它的祖先。
我们这样就可以在的时间复杂度内求出其dp值了。
之后,我们可以找到最大的集合权值,更新出以每个点为根时包含根的最大权值集合大小,如果其权值与最大权值相同,则对答案会有贡献。
不过,我们只需要一棵树,而这样会带来许多重复的,我们需要用容斥,将它的dp值减去它与其父一起的dp值即可。
然后,对于一个点所有的集合,我们用组合数可以求出它的方案数,可模数巨大,明显会爆longlong。这该怎么办呢?
笔者在这上面纠结了许久,突然发现11920928955078125这个数其实十分特别,它是。别问笔者怎么想出来的
于是我们可以用的多项式来表示,很明显,这样表示的话第24项之后的都不用取了,都被模掉了。如此,就变成了个多项式乘法,通过多项式来计算组合数,很简单就可以解决了。
源码
这道题好像卡龟速乘,被卡了好久,用__int128才过的。
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
using namespace std;
typedef long long LL;
typedef pair<LL,LL> pii;
//#define int LL
#define MAXN 65
#define MAXM 10005
#define re register
#define gc() getchar()
const LL mo=11920928955078125LL;
const LL INF=0x7f7f7f7f7f7f7f7f;
const LL phi=mo/5LL*4LL;
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=gc();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=gc();}
while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=gc();}
x*=f;
}
inline LL add(LL x,LL y){return x+y>=mo?x+y-mo:x+y;}
inline LL dec(LL x,LL y){return x-y<0?x-y+mo:x-y;}
inline LL mul(LL x,LL y){
/*LL t=0;
while(y){
if(y&1)t=add(t,x);
x=add(x,x);y>>=1;
}
return t;*/
return (__int128)x*y%mo;
}
inline LL qkpow(LL a,LL s){
LL t=1;
while(s){
if(s&1)t=mul(t,a);
a=mul(a,a);s>>=1;
}
return t;
}
inline LL inv(LL y){return qkpow(y,phi-1);}
int n,m,k,pa;
int w[MAXN],v[MAXN];
int from[MAXN<<1],to[MAXN<<1];
int paid[MAXN<<1],nxt[MAXN<<1];
int head[MAXN],tot;
int dis[MAXN][MAXN],cnt;
namespace Bignum{
LL C[25][25],pw[25];
struct poly{
LL a[25];
poly(){memset(a,0,sizeof(a));}
poly(LL d){memset(a,0,sizeof(a));a[1]=1;a[0]=d;}
friend inline poly operator * (const poly &a, const poly &b) {
poly c;
for(re int i=0;i<=23;++i)
if(b.a[i])
for(re int j=0;i+j<=23;++j)
c.a[i+j]=add(c.a[i+j],mul(a.a[j],b.a[i]));
return c;
}
void extend(LL k){
pw[0]=1;
for(re int i=1;i<=23;++i)pw[i]=mul(pw[i-1],k);
for(re int i=0;i<=23;++i){
LL tmp=0;
for(re int j=i;j<=23;++j)tmp=add(tmp,mul(a[j],mul(C[j][i],pw[j-i])));
a[i]=tmp;
}
}
}pre[10005];
void prepare(){
C[0][0]=1;
for(re int i=1;i<=23;++i){
C[i][0]=1;
for (re int j=1;j<=23;++j)
C[i][j]=add(C[i-1][j-1],C[i-1][j]);
}
pre[0].a[0]=1;
for(re int i=1;i<=10000;++i)
if(i-i/5*5) pre[i]=pre[i-1]*poly(i);
else pre[i]=pre[i-1];
}
inline poly getpoly(LL n){
if(n<=10000) return pre[n];
LL k=n/10*10;poly t1=getpoly(k>>1),t2=t1;
t2.extend(k>>1);t2=t1*t2;
for(re LL i=k+1;i<=n;++i)if(i-i/5*5)t2=t2*poly(i);
return t2;
}
inline pii calc(LL n){
poly t=getpoly(n);LL res=n/5;
if(res>0){
pii t2=calc(n/5);
t.a[0]=mul(t.a[0],t2.first);
res=add(res,t2.second);
}
return make_pair(t.a[0],res);
}
inline LL CC(LL n){
if(n<k) return 0;
pii t1=calc(n),t2=calc(n-k),t3=calc(k);
t1.second-=t2.second+t3.second;
if(t1.second>=23) return 0;
t1.first=mul(t1.first,mul(qkpow(5,t1.second),inv(mul(t2.first,t3.first))));
return t1.first;
}
}
void addEdge(int u,int v,int w){
from[++tot]=u;to[tot]=v;paid[tot]=w;
nxt[tot]=head[u];head[u]=tot;
}
void dfs_tree(int rt,int ux,int fa){
for(re int i=head[ux];i;i=nxt[i]){
int vx=to[i];if(vx==fa)continue;
dis[rt][vx]=dis[rt][ux]+paid[i];
dfs_tree(rt,vx,ux);
}
}
LL f[MAXN][MAXM],g[MAXN][MAXM];
int id[MAXN],father[MAXN],siz[MAXN];
LL maxx,Max,ans;
bool chos[MAXN];
void dfs_dp(int ux,int fa){
siz[ux]=1;id[++cnt]=ux;
for(re int i=head[ux];i;i=nxt[i]){
int vx=to[i];if(vx==fa||!chos[vx])continue;
dfs_dp(vx,ux);siz[ux]+=siz[vx];
}
}
pii DP(int x,int y){
cnt=0;dfs_dp(x,0);
for(re int i=0;i<=m;++i)f[cnt+1][i]=0,g[cnt+1][i]=1;
for(re int i=cnt;i>0;--i){
int ux=id[i];
for(re int j=0;j<=m;++j){
if(ux==y&&j<w[ux])f[i][j]=g[i][j]=0;
else if(ux==y||(j>=w[ux]&&f[i+siz[ux]][j]<f[i+1][j-w[ux]]+v[ux]))
f[i][j]=f[i+1][j-w[ux]]+v[ux],g[i][j]=g[i+1][j-w[ux]];
else if(j<w[ux]||f[i+siz[ux]][j]>f[i+1][j-w[ux]]+v[ux])
f[i][j]=f[i+siz[ux]][j],g[i][j]=g[i+siz[ux]][j];
else f[i][j]=f[i+siz[ux]][j],g[i][j]=g[i+siz[ux]][j]+g[i+1][j-w[ux]];
}
}
return make_pair(f[1][m],g[1][m]);
}
void dfs_root(int ux,int fa){
father[ux]=fa;
for(re int i=head[ux];i;i=nxt[i]){
int vx=to[i];if(vx==fa)continue;
dfs_root(vx,ux);
}
}
LL solve(int x,int y){
for(re int i=1;i<=n;++i)
if(1ll*dis[x][i]*v[i]>Max||1ll*dis[y][i]*v[i]>Max)
chos[i]=false;
else chos[i]=true;
if(!chos[x]||!(chos[y]||(!y)))return 0;
pii t=DP(x,y);if(t.first!=maxx)return 0;
return Bignum::CC(t.second);
}
signed main(){
Bignum::prepare();
read(n);read(m);read(k);read(Max);
for(re int i=1;i<=n;++i)read(w[i]);
for(re int i=1;i<=n;++i)read(v[i]);
for(re int i=1;i<n;++i){
int a,b,c;read(a);read(b);read(c);
addEdge(a,b,c);addEdge(b,a,c);
}
for(re int i=1;i<=n;++i)chos[i]=1;
for(re int i=1;i<=n;++i)dfs_tree(i,i,i);
for(re int i=1;i<=n;++i){
memset(f,0,sizeof(f));memset(g,0,sizeof(g));
pii t=DP(i,0);maxx=max(t.first,maxx);
}
if(!maxx)return !(puts("0"));
dfs_root(1,0);
for(re int i=1;i<=n;++i){
ans=add(ans,solve(i,0));
if(father[i])ans=dec(ans,solve(i,father[i]));
//printf("%lld\n",ans);
}
printf("%lld\n",ans);
return 0;
}