dp——拆分

这道题太神奇了,这么久我才理解了一点。。。。

接下来就是照打了
原题链接

这种题目考虑背包,
d p [ i ] [ j ] dp[i][j] dp[i][j]表示枚举到第i个数,乘积为j的方案总数,
而易推知 d p [ i ] 只 和 d p [ i − 1 ] dp[i]只和dp[i-1] dp[i]dp[i1]有关,用经典的背包降维,
d p [ j ] = m a x ( d p [ j ] , d p [ j / k ] + 1 ) , k dp[j]=max(dp[j],dp[j/k]+1),k dp[j]=max(dp[j],dp[j/k]+1),k表枚举可整除j的n的因数(n是输入的数)
而因为不能重复使用,我们再套用经典的逆序背包即可


但数据范围很大,所以上述做法显然是不行的(i,j都要枚举)(数组要开1e12)TLE+MLE


前置知识

即i为因子fac的下标

  • f a c < = n fac<=\sqrt{n} fac<=n 则 记录fac的下标记录为pos1=i
  • f a c > n fac> \sqrt{n} fac>n 则 记录fac的下标记录为pos2=i
  • 这样我们可以优化空间,空间复杂度降为√n

我们考虑转换状态,每次枚举可整除j的因数k,即我们可以以k为状态,枚举以k为因子j的数
但还是不行(枚举以k为因子的数j还是会TLE)
这时我们考虑枚举m,使得k*m=j
但为了避免重复计算,我们考虑不妨设k为j的最大因子
由于有了前置知识,所以我们可以只枚举编号,用 f a c 和 p o s fac和pos facpos 转换,

dp[i][j] 表示将编号为i的因子fac[i]拆分,使拆分到的每一个数均<=fac[j]

为了方便记录初始状态,我们不妨记录每一个数都可以拆成自己,最后减1即可,
(因为比如42=67) 我们可以用6,7转移,但7不能拆,方案数为0,而根据乘法原理01=0,显然不对

讨论dp[i][j]的转移
d p [ i ] [ j ] + = d p [ i ] [ j − 1 ] dp[i][j]+=dp[i][j-1] dp[i][j]+=dp[i][j1] 直接继承,分不到 f a c j fac_j facj,即分 f a c i < = f a c j − 1 fac_i<=fac_{j-1} faci<=facj1的个数
i f ( i = = j ) d p [ i ] [ j ] + + if(i==j)dp[i][j]++ if(i==j)dp[i][j]++ 算上这个数本身
i f if if ( f a c i (fac_i (faci % f a c j = = 0 ) fac_j==0) facj==0) f a c i fac_i faci f a c j fac_j facj这个因子,由于我们没统计过含有 f a c j fac_j facj的个数,那我们
不妨提出 f a c j fac_j facj,又因子不能重复,将所有 f a c [ i ] / f a c [ j ] fac[i]/fac[j] fac[i]/fac[j]中的个数统计出来(这些方案中一定不含有 f a c j fac_j facj,而所有的方案乘fac[j]就是含有fac[j]的方案)我们只需要用 d p [ p o s ( f a c [ i ] / f a c [ j ] ) ] [ j − 1 ] dp[pos(fac[i]/fac[j])][j-1] dp[pos(fac[i]/fac[j])][j1]更新即可,

if(fac[i]%fac[j]==0) {
			ll tmp=fac[i]/fac[j],tmpn;
			if(tmp<=sqr) tmpn=pos1[tmp];
			if(tmp>sqr)  tmpn=pos2[n/tmp];
			dp[i][j]=(dp[tmpn][j-1]+dp[i][j])%mod;
		}

时间复杂度虽然不低,但能卡着AC

#include
using namespace std;

typedef long long ll;
const ll N=(1000000);
ll n;
ll tnt=0,dp[7720][7720];
ll fac[7720];
ll mod=998244353;
ll pos1[N],pos2[N];
int main(){
	ll T;
	scanf("%lld",&T);

	while(T--){
	tnt=0;
	scanf("%lld",&n);
	int sqr=sqrt(n);
    for(ll i=1;i<=sqr;i++){
	    if(n%i==0){
	    	fac[++tnt]=i;
	    	fac[++tnt]=n/i;
	    }
	}
	if(sqr*sqr==n){	tnt--;}  
    sort(fac,fac+tnt+1);
    for(ll i=0;i<=tnt;i++)
    for(ll j=0;j<=tnt;j++) dp[i][j]=0;
	for(ll i=1;i<=tnt;i++){
		dp[i][i]=1;
		if(fac[i]<=sqr) pos1[fac[i]]=i;
		if(fac[i]>sqr)  pos2[n/fac[i]]=i;
	}
	for(ll i=1;i<=tnt;i++)
	for(ll j=1;j<=tnt;j++){
		dp[i][j]=(dp[i][j-1]+dp[i][j])%mod;
		if(i<=j){continue;}	
		if(fac[i]%fac[j]==0) {
			ll tmp=fac[i]/fac[j],tmpn;
			if(tmp<=sqr) tmpn=pos1[tmp];
			if(tmp>sqr)  tmpn=pos2[n/tmp];
			dp[i][j]=(dp[tmpn][j-1]+dp[i][j])%mod;
		}
	}
	printf("%lld\n",(dp[tnt][tnt]-1+mod)%mod);  
   }
}

你可能感兴趣的:(例题,动态规划)