• 【LOJ2462】「2018 集训队互测 Day 1」完美的集合(树上连通块问题+扩展卢卡斯)


    点此看题面

    • 给定一棵(n)个点的树,每个点有一个重量(w_i)和一个价值(v_i),每条边有一个长度。
    • 对所有(w_i)之和不超过(m)的树上连通块,定义其中(v_i)之和最大的那些连通块对应的点集为完美的集合。
    • 现要从这些完美的集合中选出(k)个,找到一个关键点(x)位于(k)个点集的交集中,且(x)与这(k)个点集的并集中任意一点(y)满足(dis(x,y) imes v_yle)给定的参数(lim)
    • 求有多少种选择集合的方式,使得存在至少一个符合条件的关键点(x)
    • (nle60,mle10^4,kle10^9),模数为(5^{23})

    树上连通块=点-边

    发现判定条件(dis(x,y) imes v_y)乘上的系数是(v_y)而非(v_x),故实际上我们可以保证对于选中的(k)个点集,所有符合条件的关键点(x)构成一个连通块。(证明:任意两个关键点间路径上的点肯定比它们更符合条件)

    对于这种树上连通块问题,由于连通块内各点是相互独立的,有一个常见的容斥计算方式,就是用点的贡献减去边的贡献。

    具体地,我们先求出每个点作为关键点的方案数,再减去每条边上两点能够同时作为关键点的方案数,也就是用关键点数减去关键边数,便得到了关键连通块数。

    树上连通块经典(DP)

    要求点(x)作为关键点的方案数时,我们仅保留能被它测试的点。

    然后就是一个经典的树上连通块(DP),设(f_{i,j})表示(DP)(dfs)序列上第(i)位,已占重量为(j)时的最大价值和(可以绑成(struct)同时维护方案数),转移考虑两种情况:

    • 当前位置选择,直接更新重量和及价值和转移到第(i+1)位。
    • 当前位置不选,则它子树内都不能选,由于子树在(dfs)序列上对应一段区间,直接转移到这段区间右端点(+1)的位置即可。

    如果是要求((x,y))作为关键边的方案数,就仅保留能同时被它俩测试的点。因为它俩都是必选的,可以任选其中一个为根,并在枚举到它们时强制执行第一种转移即可。

    我们一开始先不管测试,保留整棵树,枚举每个点作根,利用上面的(DP)求出完美集合的价值之和(Mx)。之后只要判断价值之和是否为(Mx)即可判断是否为完美的集合了。

    组合数取模

    取模问题是这道题的又一大难点。。。

    要求(C_x^k),只需分别求出(x!,k!,(x-k)!),并把它们表示成(v imes 5^p)的形式。

    首先有(n!=prod_{i=1}^n[iperp 5]i imes (lfloorfrac n5 floor! imes 5^{lfloorfrac n5 floor})),式子中(lfloorfrac n5 floor!)可以递归求解,因此主要问题就变成了求(prod_{i=1}^n[iperp 5]i)

    (F_n(x)=prod_{i=1}^n[iperp5](x+i)),我们要求的就是(F_n(0))

    考虑倍增,(F_{10a}(x)=F_{5a}(x)*F_{5a}(x+5a))。其中(F_{5a}(x+5a))可以用二项式定理展开,因为模数是(5^{23}),所以只需保留第(0sim 22)次项即可。

    这样就能求出(F_{10 imes lfloorfrac n{10} floor}(x)),而它与(F_n(x))只相差不到(10)个二项式,直接暴力卷上就好了。

    代码:(O()跑不满())

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 60
    #define M 10000
    #define X 11920928955078125
    #define LL long long
    #define add(x,y,z) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].l=z)
    using namespace std;
    int n,m,k,w[N+5],v[N+5];LL lim;int ee,lnk[N+5];struct edge {int to,nxt,l;}e[2*N+5];
    I LL QM(LL x,LL y,LL p) {LL k=x*y-(LL)(1.0L*x*y/p)*p;return (k%p+p)%p;}
    I LL QP(LL x,LL y,LL p) {LL t=1;W(y) y&1&&(t=QM(t,x,p)),x=QM(x,x,p),y>>=1;return t;}
    namespace FastIO
    {
    	#define FS 100000
    	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
    	char oc,FI[FS],*FA=FI,*FB=FI;
    	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
    	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    }using namespace FastIO;
    namespace S//组合数取模
    {
    	LL p,X_,C[23][23];I void InitC()//预处理组合数
    	{
    		RI i,j;for(C[0][0]=i=1;i^p;++i) for(C[i][0]=j=1;j^p;++j) C[i][j]=(C[i-1][j-1]+C[i-1][j])%X_;
    	}
    	struct Poly
    	{
    		LL v[23];I Poly() {memset(v,0,sizeof(v));}I Poly(Con LL& x) {memset(v,0,sizeof(v)),v[0]=x%X_,v[1]=1;}
    		I LL& operator [] (CI x) {return v[x];}I LL operator [] (CI x) Con {return v[x];}
    		I Poly operator * (Con Poly& o) Con//暴力卷积
    		{
    			Poly t;for(RI i=0;i^p;++i) for(RI j=0;j^(p-i);++j) (t[i+j]+=QM(v[i],o[j],X_))%=X_;return t;
    		}
    	};
    	I Poly Upt(Con Poly& f,Con LL& x)//二项式定理展开
    	{
    		Poly t;RI i,j;LL w;for(i=0;i^p;++i) for(w=1,j=0;
    			j<=i;w=QM(w,x,X_),++j) (t[i-j]+=QM(f[i],QM(C[i][j],w,X_),X_))%=X_;return t;
    	}
    	I Poly Solve(Con LL& x)//倍增
    	{
    		if(!x) {Poly F;return F[0]=1,F;}LL w=x/10*10;Poly F=Solve(w/2);F=F*Upt(F,w/2);//倍增
    		for(LL i=w+1;i<=x;++i) i%5&&(F=F*Poly(i),0);return F;//暴力卷上多余项
    	}
    	I LL Fac(LL x) {LL t=1;W(x) t=QM(t,Solve(x)[0],X_),x/=5;return t;}//除去5之后的阶乘
    	I LL Cnt(LL x) {LL t=0;W(x) t+=(x/=5);return t;}//阶乘中5的幂次
    	I LL Calc(Con LL& x)//计算C(x,k)
    	{
    		if(x<k) return 0;p=23-(Cnt(x)-Cnt(k)-Cnt(x-k));if(p<=0) return 0;X_=QP(5,p,X+1),InitC();//给模数除去5的幂次
    		LL A=Fac(x),B=QM(Fac(k),Fac(x-k),X_);return QM(A,QP(B,X_/5*4-1,X_),X_)*(X/X_);//计算Fac(x)/Fac(k)/Fac(x-k)
    	}
    }
    namespace T//树上连通块问题
    {
    	int q[N+5],dis[N+5][N+5];I void bfs(CI x)//预处理距离
    	{
    		RI i,k,H=1,T=1;q[1]=x;W(H<=T) for(i=lnk[k=q[H++]];i;i=e[i].nxt)
    			e[i].to^x&&!dis[x][e[i].to]&&(dis[x][q[++T]=e[i].to]=dis[x][k]+e[i].l);
    	}
    	int p[N+5],d,dI[N+5],dO[N+5],s[N+5];I void dfs(CI x,CI lst=0)//遍历求dfs序
    	{
    		s[dI[x]=++d]=x;for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&p[e[i].to]&&(dfs(e[i].to,x),0);dO[x]=d;
    	}
    	struct node
    	{
    		LL v,c;I node(Con LL& a=-1,Con LL& b=0):v(a),c(b){}
    		I node operator + (CI o) {return node(v+o,c);}
    		I void operator += (Con node& o) {v^o.v?v<o.v&&(v=o.v,c=o.c):c+=o.c;}
    	}f[N+5][M+5];
    	I node DP(CI x,CI y)//树上连通块经典DP
    	{
    		RI i,j;for(d=0,dfs(x),i=1;i<=d+1;++i) for(j=0;j<=m;++j) f[i][j]=node();//清空
    		for(f[1][0]=node(0,1),i=1;i<=d;++i) for(j=0;j<=m;++j) f[i][j].c&&
    			(j+w[s[i]]<=m&&(f[i+1][j+w[s[i]]]+=f[i][j]+v[s[i]],0),s[i]^x&&s[i]^y&&(f[dO[s[i]]+1][j]+=f[i][j],0));//选;不选
    		node res;for(j=0;j<=m;++j) res+=f[d+1][j];return res;//统计总答案
    	}
    	LL Mx;I LL Solve(CI x,CI y)//计算关键点(y=0)或关键边的贡献
    	{
    		RI i,j;for(i=1;i<=n;++i) p[i]=1LL*v[i]*max(dis[x][i],dis[y][i])<=lim;//仅保留能测试的点
    		if(!p[x]||(y&&!p[y])) return 0;node t=DP(x,y);return t.v^Mx?0:S::Calc(t.c);//返回C(t.c,k)
    	}
    	I LL GetAns()
    	{
    		RI i;LL t=0;for(i=1;i<=n;++i) p[i]=1;for(i=1;i<=n;++i) Mx=max(Mx,DP(i,0).v);//不管测试,求出完美集合的价值之和
    		for(i=1;i<=n;++i) (t+=Solve(i,0))%=X;for(i=1;i<=ee;i+=2) (t+=X-Solve(e[i].to,e[i+1].to))%=X;return t;//点-边
    	}
    }
    int main()
    {
    	RI i;for(read(n,m,k,lim),i=1;i<=n;++i) read(w[i]);for(i=1;i<=n;++i) read(v[i]);
    	RI x,y,z;for(i=1;i^n;++i) read(x,y,z),add(x,y,z),add(y,x,z);
    	for(i=1;i<=n;++i) T::bfs(i);return printf("%lld
    ",T::GetAns()),0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    mac下配置adb环境变量
    Flutter——多行文字展开收起
    sourceTree报错:Updates were rejected because the tag already exists in the remote.
    股市赚钱就是这么简单
    鸿蒙手机版JNI实战(JNI开发、SO库生成、SO库使用)
    Mybatis、Mybatis Generator、Mybatis-Plus、Mybatis Plus Generator
    鸿蒙运行报错:Failure[INSTALL_PARSE_FAILED_USESDK_ERROR] Error while Deploying HAP
    MAC 下Android ROM 提取文件
    IDAPython 插件开发
    Android ollvm 集成
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/LOJ2462.html
Copyright © 2020-2023  润新知