[LOJ 2462][2018 集训队互测 Day 1]完美的集合

完美的集合

题解

生平最讨厌的卡常。

其实这道题蛮好想的。

由题可知,如果一个集合能用,它一定是一个联通块而且总重量小于m。很明显的dp了,数据范围也不大。

我们可以先以每个节点为根,都做一次dp。

不过我们马上发现,时间复杂度是O\left(n^{2}m^{2} \right ),明显要超时。那我们应该怎么做呢?

我们可以采取dfs序dp。因为我们只需求出每个根的dp值,那么其它点的dp值我们都是可以不管的,也就是它是什么值都无所谓。那么我们就将每个点的子树大小记录下来,对于一个点,如果它的dfs序为i,那么ii+ siz_{i}- 1属于它的子树。而它的dfs序减去它父亲下一个儿子的子树大小就是他父亲下一个儿子的dfs序。如此我们就可以将上一个儿子的dp值与下一个儿子的dp值放在一起,一直更新到它的祖先。

我们这样就可以在O\left(n^{2}m \right )的时间复杂度内求出其dp值了。

之后,我们可以找到最大的集合权值,更新出以每个点为根时包含根的最大权值集合大小,如果其权值与最大权值相同,则对答案会有贡献。

不过,我们只需要一棵树,而这样会带来许多重复的,我们需要用容斥,将它的dp值减去它与其父一起的dp值即可。

然后,对于一个点所有的集合,我们用组合数可以求出它的方案数,可模数巨大,明显会爆longlong。这该怎么办呢?

笔者在这上面纠结了许久,突然发现11920928955078125这个数其实十分特别,它是5^{23}别问笔者怎么想出来的

于是我们可以用F\left(x \right )=f_{0}1+f_{1}5+...+f_{23}5^{23}的多项式来表示,很明显,这样表示的话第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;
}

谢谢!!!

发布了117 篇原创文章 · 获赞 154 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Tan_tan_tann/article/details/103722085