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\) 右子树中.

\[f(x,l,r) =\max_{l-1\le k\le r} f(x_{lson},l,k) + f(x_{rson},k+1,r)+\sum_{i=k+1}^r a_i \]

但这棵 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;
}

posted on 2020-06-16 21:28  autoint  阅读(174)  评论(0编辑  收藏  举报

导航