• P4133 [BJOI2012]最多的方案 二分+DP


    题意:

    给定(n),将(n)用一些斐波那契数的和表示,求有多少种表示方法,使得每种方案里不会出现相同的两个数

    范围&性质:(1le nle 10^{18})

    分析:

    由于每一个数只能用至多一次,所以题目就转换成了一个01背包问题,但是这个背包的物品体积和容量过大,所以我们需要考虑优化,首先由于斐波那契数增长极快,第91项时已经超过了(10^{18}),所以我们只要先预处理出前91项,之后为了保证方案不重复,我们每次枚举删掉(n)范围内最大的斐波那契数,然后用小于ta的所有数去拼成剩下的部分,递归处理,这样可以保证方案不会重复

    我们记(f[i][j])表示用前(i)个数表示出(j)的最大方案数,转移式为(dp[i][j]=sum dp[k-1][i-f[k]]),k的取值的下界就是当(sum[k])(表示前(k)个数的和)小于(i)是不能选,因为至少保证前(k)个数都选时方案存在,所以每次枚举(k)时lower_bound求出(k)的下界开始枚举

    代码:

    #include<bits/stdc++.h>
    
    using namespace std;
    
    namespace zzc
    {
    	long long f[100],sum[100];
    	map<long long,long long> mp[100];
    	long long n;
    	
    	void init()
    	{
    		f[1]=1;
    		f[2]=2;
    		for(int i=3;i<=91;i++)
    		{
    			f[i]=f[i-1]+f[i-2];
    		}
    		for(int i=1;i<=89;i++)
    		{
    			sum[i]=sum[i-1]+f[i];
    		}
    		sum[90]=sum[91]=0x7fffffffffff;
    		for(int i=0;i<=91;i++)
    		{
    			mp[i][0]=1;
    		}
    	}
    	
    	void dfs(long long x,int lim)
    	{
    		if(mp[lim].count(x)) return ;
    		int tmp=lower_bound(sum+1,sum+91+1,x)-sum;
    		for(int i=tmp;i<=lim;i++)
    		{
    			if(x-f[i]<0) break;
    			dfs(x-f[i],i-1);
    			mp[lim][x]+=mp[i-1][x-f[i]];
    		}
    	}
    	
    	void work()
    	{
    		init();
    		scanf("%lld",&n);
    		dfs(n,91);
    		printf("%lld",mp[91][n]);
    	}
    	
    }
    
     int main()
     {
     	zzc::work();
     	return 0;
     }
    
  • 相关阅读:
    putty的复制 技巧
    linux下的yum命令详解
    mysql修改密码
    我的阅读编程书籍的好方法
    WINDOWS下VIM配置
    Debian下VSFTPD配置
    一个远程访问MySQL的错误(2003, 10061)的解决
    auto_increment
    hello,world!
    scss文件中使用深度选择器/deep/报错 Expected selector Jim
  • 原文地址:https://www.cnblogs.com/youth518/p/13694585.html
Copyright © 2020-2023  润新知