Codeforces 1188D Make Equal DP
题意:给你个序列,你可以给某个数加上2的幂次,问最少多少次可以让所有的数相等。
思路(官方题解):我们先给序列排序,假设bit(c)为c的二进制数中1的个数,假设所有的数最后都成为了x, 显然x >= a[n],那么最后的总花费为Σbit(x - a[i])。不妨假设x = t + a[n], b[i] = a[n] - a[i], 那么问题转化为了求Σbit(t + b[i])的最小值。我们假设最后取得最小值的数是x。我们假设已经知道了填充x的低k - 1位的最小花费,我们来考虑填充第k位。我们发现,知道第k位对答案的贡献需要知道以下3项:1:b序列在第k位的二进制位。2:低位的进位。3:这一位x是填0还是填1。对于第一项,我们已经知道了。第三项在dp的时候直接判断转移就行,现在的问题是第二项。乍一看,我们需要2 ^ n个状态来表示上一位的进位情况。仔细想一想发现,因为这一位要么填0,要么填1,即要么不加数,要么加同一个数。所以,我们对所有的数取模 2 ^ k后从小到大排序,最后产生进位的位置是一个后缀,这样我们就可以把状态数降成O(n)个。我们假设dp[i][j]为填x的第i位,上一位产生进位的后缀长度为j的最小花费,转移的时候判断这位填0还是填1就可以了。dp的复杂度是O(n * log(n) * log(max(a)), 为什么是log(max(a))呢?因为t的大小不会大于b[1] = a[n] - a[1]。官方题解有证明,但是我们可以直观感受一下这是对的:当t正好等于b[1]时, 相当于是b[1] << 1, 之后的操作会重复,并且不会使答案更小。
采用基于二进制数的基数排序可以使复杂度进一步降低为O(n * log(max(a))。
代码(基数排序版本):
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn = 100010;
LL dp[70][maxn];
LL a[maxn], f[maxn], f1[maxn];
int cnt;
int sum0[maxn], sum1[maxn], tot0, tot1, n;
void Sort(LL x) {
cnt = 0;
for (int i = 1; i <= n; i++)
if(((a[f[i]] >> x) & 1) == 0) f1[++cnt] = f[i];
for (int i = 1; i <= n; i++)
if((a[f[i]] >> x) & 1) f1[++cnt] = f[i];
for (int i = 1; i <= n; i++)
f[i] = f1[i];
}
void init(LL x) {
for (int i = 1; i <= n; i++) {
sum0[i] = sum0[i - 1];
sum1[i] = sum1[i - 1];
if((a[f[i]] >> x) & 1) sum1[i]++;
else sum0[i]++;
}
tot1 = sum1[n], tot0 = sum0[n];
}
int main() {
LL mx = 0;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
mx = max(mx, a[i]);
}
for (int i = 1; i <= n; i++) {
a[i] = mx - a[i];
}
memset(dp, 0x3f, sizeof(dp));
for (int i = 1; i <= n; i++) f[i] = i;
dp[0][0] = 0;
for (LL i = 0; i < 62; i++) {
if(i != 0) {
Sort(i - 1);
}
init(i);
for (int j = 0; j <= n; j++) {
for (int bit = 0; bit < 2; bit++) {
if(bit == 0) {
int val = sum1[n - j] + tot0 - sum0[n - j], Next_state = tot1 - sum1[n - j];
dp[i + 1][Next_state] = min(dp[i + 1][Next_state], dp[i][j] + val);
} else if(bit == 1) {
int val = sum0[n - j] + tot1 - sum1[n - j], Next_state = n - sum0[n - j];
dp[i + 1][Next_state] = min(dp[i + 1][Next_state], dp[i][j] + val);
}
}
}
}
printf("%lld\n", dp[62][0]);
}
官方题解有一些证明,代码也比较易懂。
官方题解:http://codeforces.com/blog/entry/68079
官方题解代码:https://pastebin.com/usq1czKq
Codeforces 1188D Make Equal DP的更多相关文章
- Codeforces 1188D - Make Equal(dp)
Codeforces 题目传送门 & 洛谷题目传送门 首先我们考虑枚举最后这 \(n\) 个数变成的值 \(v\),那么需要的操作次数即为 \(\sum\limits_{i=1}^n\text ...
- [Codeforces 1201D]Treasure Hunting(DP)
[Codeforces 1201D]Treasure Hunting(DP) 题面 有一个n*m的方格,方格上有k个宝藏,一个人从(1,1)出发,可以向左或者向右走,但不能向下走.给出q个列,在这些列 ...
- Codeforces 622C Not Equal on a Segment 【线段树 Or DP】
题目链接: http://codeforces.com/problemset/problem/622/C 题意: 给定序列,若干查询,每个查询给定区间和t,输出区间内任意一个不等于t的元素的位置. 分 ...
- codeforces Hill Number 数位dp
http://www.codeforces.com/gym/100827/attachments Hill Number Time Limits: 5000 MS Memory Limits: ...
- codeforces Educational Codeforces Round 16-E(DP)
题目链接:http://codeforces.com/contest/710/problem/E 题意:开始文本为空,可以选择话费时间x输入或删除一个字符,也可以选择复制并粘贴一串字符(即长度变为两倍 ...
- codeforces #round363 div2.C-Vacations (DP)
题目链接:http://codeforces.com/contest/699/problem/C dp[i][j]表示第i天做事情j所得到最小的假期,j=0,1,2. #include<bits ...
- codeforces round367 div2.C (DP)
题目链接:http://codeforces.com/contest/706/problem/C #include<bits/stdc++.h> using namespace std; ...
- CodeForces 176B Word Cut dp
Word Cut 题目连接: http://codeforces.com/problemset/problem/176/C Description Let's consider one interes ...
- CodeForces 455A Boredom (DP)
Boredom 题目链接: http://acm.hust.edu.cn/vjudge/contest/121334#problem/G Description Alex doesn't like b ...
随机推荐
- 在linux终端中清空文件
cat /dev/null > file_name vim file_name 把文件的前10行拷贝到新的文件中 head -n10 file_name1 > file_name2
- bzoj4007 & loj2111 [JLOI2015]战争调度 复杂度分析+树上背包
题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4007 https://loj.ac/problem/2111 题解 同 [NOI2006]网络 ...
- python输出转义字符
转义字符在字符串中不代表自己,比如\n代表回车,不代表\n字符,那我想输入转义字符本身呢? 答:在字符串前面加个r 如print(“aa\nbb”) 会输出aa bb 如print(r"aa ...
- HTTP Error 500.30 - ANCM In-Process Start Failure
环境 windown 10 IIS 10 net core 2.2 vs2019 背景 在vs2019使用net core 2.2发布到IIS 10上(见在 ASP.NET Core 中使用多个环境) ...
- UiAutomator和Appium之间的区别2
UiAutomator和Appium之间的区别和联系 联系: 在Android端,appium基于WebDriver协议,利用Bootstrap.jar,最后通过调⽤用UiAutomator的命令,实 ...
- Git 如何删除分支
- 2019 GNTC 阿里云参会分享:开放、弹性的阿里云网络NFV平台
作为全球规模最大的网络技术盛会之一,GNTC全球网络技术大会是网络技术发展的重要风向标,包含战略规划.产业方向.技术趋势.应用创新等皆汇集于此.而作为云服务商代表,阿里云再度受邀以顶级钻石合作伙伴之名 ...
- 【bzoj1185】[HNOI2007]最小矩形覆盖 (旋转卡壳)
给你一些点,让你用最小的矩形覆盖这些点 首先有一个结论,矩形的一条边一定在凸包上!!! 枚举凸包上的边 用旋转卡壳在凸包上找矩形另外三点... 注意精度问题 #include<cstdio> ...
- C#后台获取post参数
public static string GetQueryString(string key) { if (HttpContext.Current.Request[key] == null) retu ...
- 洛谷 4099 [HEOI2013]SAO——树形DP
题目:https://www.luogu.org/problemnew/show/P4099 结果还是看了题解才会…… 关键是状态,f[ i ][ j ] 表示 i 子树. i 号点是第 j 个出现的 ...