CF1428G Lucky Numbers


一、题目

点此看题

二、解法

首先观察权值完全是由数位决定的,我们考虑按位规划,每一位可以选不超过 \(3k\)\(3\),每一个 \(3\) 贡献是 \(F_i\)

这样算出的结果很可能不合法,有些数位是会为了满足题目限制而选取 \(0,3,6,9\) 以外的数。

结论:最优解中每个数位最多有一个选取 \(0,3,6,9\) 以外的数。因为如果出现了两个及以上可以通过调整只剩一个,而且调整之后的解一定更优。

那么把所有特别的数位都集中在一个数里,设 \(dp[i]\) 为总和为 \(i\) 的最优解,那么初始化 \(dp[x]\) 为数字 \(x\) 的权值,也就是直接把这个数考虑了。那么剩下的问题可以按位规划了,每一位可以选不超过 \(3k-3\)\(3\),每一个 \(3\) 贡献是 \(F_i\)

不难发现这是一个多重背包问题,可以二进制优化多重背包,时间复杂度 \(O(n\log n)\)

三、总结

还是解决限制类问题,可以通过特殊考虑使问题变简单。

背包问题的特殊结论:有一个物品是特殊的,它的选取方式和其他物品不一样,特殊考虑它就能让问题变简单。

#include 
#include 
using namespace std;
const int M = 1000005;
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,k,q,a[M],dp[M];
void get(int v,int c)
{
    for(int i=n;i>=v;i--)
        dp[i]=max(dp[i],dp[i-v]+c);
}
void work(int v,int c)
{
    int now=min(k,n/v);
    for(int i=1;i

相关