LOJ3215 Muzyka pop
Muzyka pop
给定 \(n\) 个整数 \(a_1, a_2, \dots, a_n\) 和一个整数 \(m\)。请找到 \(n\) 个非负整数 \(b_1, b_2, \ldots, b_n\),满足 \(0 \le b_1 < b_2 < \ldots < b_n \le m\) 并且 \(\sum\limits_{i=1}^{n} \text{popcount}(b_i) \cdot a_i\) 的值最大,其中 \(\text{popcount}(x)\) 为 \(x\) 在二进制下的 \(1\) 的个数。
\(1 \le n \le 200, n - 1 \le m \le 10^{18}, 1 \le |a_i| \le 10^{14}\)
题解
http://jklover.hs-blog.cf/2020/06/08/Loj-3215-Muzyka-pop/#more
数位 dp.
假定有一棵插入了 \([0,m]\) 内所有数的 01 Trie 树,我们可以在上面做简单的 dp 求出答案.
设 \(f(x,l,r)\) 表示把 \([l,r]\) 内所有的 \(b\) 都分配入 \(x\) 的子树中能产生的最大贡献.
转移时枚举将 \([l,k]\) 内的 \(b\) 分入 \(x\) 左子树中, \([k+1,r]\) 内的 \(b\) 分入 \(x\) 右子树中.
但这棵 Trie 树的节点数目是 \(O(m)\) 的,直接这样 dp 不可行.
注意到除去从 \(m\) 到根这条链上的点,其它点的每个子树都是完全二叉树.
于是 dp 只需要记录当前节点深度,以及是否在从 \(m\) 到根的链上.
其实这东西就是个数位 dp, 深度表示考虑到了哪一位,是否在链上表示是否顶住了上界.
用 Trie 树的形式可能比较容易理解贡献的计算.
时间复杂度 \(O(n^3\log m)\) .
CO int N=210;
CO int64 inf=1e18;
int64 sum[N],f[60][2][N][N];
int main(){
int n=read<int>();
int64 m=read<int64>();
int K=log2(m);
for(int i=1;i<=n;++i) sum[i]=sum[i-1]+read<int64>();
for(int i=0;i<=K;++i)for(int lim=0;lim<=1;++lim){
int t=lim and ~m>>i&1; // unable to go right
for(int l=1;l<=n;++l)for(int r=l;r<=n;++r){
int mi=t?r:l-1;
int64 val=-inf;
for(int k=mi;k<=r;++k){
int64 tmp=sum[r]-sum[k];
if(i){
tmp+=f[i-1][t][l][k];
tmp+=f[i-1][lim][k+1][r];
}
else if(k-l+1>1 or r-k>1) continue; // pairwise distinct
val=max(val,tmp);
}
f[i][lim][l][r]=val;
}
}
printf("%lld\n",f[K][1][1][n]);
return 0;
}