Codeforces 1188D - Make Equal(dp)
首先我们考虑枚举最后这 \(n\) 个数变成的值 \(v\),那么需要的操作次数即为 \(\sum\limits_{i=1}^n\text{bitcnt}(v-a_i)\),其中 \(\text{bitcnt}(x)\) 为 \(x\) 二进制中 \(1\) 的个数。
这样似乎不太好直接求,不过不难发现这个 \(v\) 一定是 \(\ge\max\{a_i\}\) 的,因此我们考虑转而枚举 \(x=v-\max\{a_i\}\),我们记 \(b_i=\max\{a_j\}-a_i\),那么操作次数又可写为 \(\sum\limits_{i=1}\text{bitcnt}(b_i+x)\)。
显然最优方案下的 \(x\) 必然 \(< 2^{60}\),否则我们总可以找到某一位 \(y\ge 60\) 满足 \(x\) 的 \(2^y\) 为 \(1\),此时考虑令 \(x\leftarrow x-2^y\),答案不会变得更劣。因此我们考虑按位决策,即先考虑 \(x\) 二进制下的最低位,再考虑二进制下的次低位,以此类推。对于 \(x\) 的每一位 \(2^p\),它对答案产生的影响显然就是满足 \(a_i+x\) 的 \(2^p\) 位为 \(1\) 的 \(i\) 的个数,考虑这个个数的影响因素,显然这东西会受到以下三点的影响:
- 每个 \(a_i\) 的 \(2^p\) 位上的值
- \(x\) 的 \(2^p\) 位上的值
- \(a_i+x\) 在 \(2^{p-1}\) 位是否产生了进位
第一点显然开个桶记录一下就行了,第二点就直接枚举 \(x\) 的 \(2^p\) 位是 \(0\) 还是 \(1\) 并分别计算一下即可。棘手的地方在于第三点,共 \(2^n\) 个状态,如果简简单单将其放入 \(dp\) 状态中那连暴力都跑不过,完全不能接受。
不过注意到我们所加的数 \(x\) 是相同的。稍微想想就能知道,\(a_i\bmod 2^p\) 越大的肯定越容易产生进位。因此倘如我们将所有 \(a_i\) 按 \(a_i\bmod 2^p\) 从小到大排序,产生进位的 \(a_i\) 必定是一段后缀。也就是说只要知道有多少个 \(a_i+x\) 在 \(2^{p-1}\) 位产生了进位,就能知道是哪些 \(a_i\) 产生了进位。因此我们设 \(dp_{i,j}\) 表示考虑了前 \(i\) 位,当前位有 \(j\) 个产生进位的最小 \(1\) 的个数,考虑转移,我们假设已经确定了 \(x\) 的前 \(i\) 位,有 \(j\) 个数产生进位,那么按照 \(a_t+x\) 是否在 \(2^i\) 位产生进位,以及 \(a_t\) 的 \(2^{i+1}\) 位的取值可分为 \(4\) 类:
- \(a_t+x\) 在 \(2^i\) 位产生了进位且 \(a_t\) 的 \(2^{i+1}\) 位为 \(1\)
- \(a_t+x\) 在 \(2^i\) 位产生了进位且 \(a_t\) 的 \(2^{i+1}\) 位为 \(0\)
- \(a_t+x\) 在 \(2^i\) 位没有产生进位且 \(a_t\) 的 \(2^{i+1}\) 位为 \(1\)
- \(a_t+x\) 在 \(2^i\) 位没有产生进位且 \(a_t\) 的 \(2^{i+1}\) 位为 \(0\)
我们枚举 \(x\) 的 \(2^{i+1}\) 位是什么,那么
- \(x\) 的 \(2^{i+1}\) 位为 \(0\),那么第一类数会产生进位,第二、三类数 \(2^{i+1}\) 位为 \(1\),贡献随便算
- \(x\) 的 \(2^{i+1}\) 位为 \(1\),那么第一、二、三类数会产生进位,第一、四类数 \(2^{i+1}\) 位为 \(1\)。
时间复杂度 \(n\log^2n\)。
const int MAXB=60;
const int MAXN=1e5;
int n,ord[MAXN+5];ll a[MAXN+5],b[MAXN+5];
int dp[MAXB+5][MAXN+5],sum[MAXN+5][2];
int main(){
scanf("%d",&n);for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
sort(a+1,a+n+1);for(int i=1;i<=n;i++) a[i]=a[n]-a[i];
memset(dp,63,sizeof(dp));dp[0][0]=0;
for(int i=0;i<=MAXB;i++){
memset(sum,0,sizeof(sum));
for(int j=1;j<=n;j++) b[j]=a[j]&((1ll<<i)-1);
for(int j=1;j<=n;j++) ord[j]=j;
sort(ord+1,ord+n+1,[](int x,int y){return b[x]<b[y];});
for(int j=1;j<=n;j++){
sum[j][0]=sum[j-1][0];sum[j][1]=sum[j-1][1];
sum[j][a[ord[j]]>>i&1]++;
}
for(int j=0;j<=n;j++){
int cst=sum[n-j][1]+sum[n][0]-sum[n-j][0],carry=sum[n][1]-sum[n-j][1];
chkmin(dp[i+1][carry],dp[i][j]+cst);
cst=sum[n-j][0]+sum[n][1]-sum[n-j][1],carry=n-sum[n-j][0];
chkmin(dp[i+1][carry],dp[i][j]+cst);
}
} printf("%d\n",dp[MAXB+1][0]);
return 0;
}
Codeforces 1188D - Make Equal(dp)的更多相关文章
- Codeforces Gym101341K:Competitions(DP)
http://codeforces.com/gym/101341/problem/K 题意:给出n个区间,每个区间有一个l, r, w,代表区间左端点右端点和区间的权值,现在可以选取一些区间,要求选择 ...
- codeforces 711C Coloring Trees(DP)
题目链接:http://codeforces.com/problemset/problem/711/C O(n^4)的复杂度,以为会超时的 思路:dp[i][j][k]表示第i棵数用颜色k涂完后bea ...
- codeforces#1154F. Shovels Shop (dp)
题目链接: http://codeforces.com/contest/1154/problem/F 题意: 有$n$个物品,$m$条优惠 每个优惠的格式是,买$x_i$个物品,最便宜的$y_i$个物 ...
- Codeforces 1051 D.Bicolorings(DP)
Codeforces 1051 D.Bicolorings 题意:一个2×n的方格纸,用黑白给格子涂色,要求分出k个连通块,求方案数. 思路:用0,1表示黑白,则第i列可以涂00,01,10,11,( ...
- Codeforces 1207C Gas Pipeline (dp)
题目链接:http://codeforces.com/problemset/problem/1207/C 题目大意是给一条道路修管道,相隔一个单位的管道有两个柱子支撑,管道柱子高度可以是1可以是2,道 ...
- Codeforces 704C - Black Widow(dp)
Codeforces 题目传送门 & 洛谷题目传送门 u1s1 感觉这种题被评到 *2900 是因为细节太繁琐了,而不是题目本身的难度,所以我切掉这种题根本不能说明什么-- 首先题目中有一个非 ...
- Codeforces 682B New Skateboard(DP)
题目大概说给一个数字组成的字符串问有几个子串其代表的数字(可以有前导0)能被4整除. dp[i][m]表示字符串0...i中mod 4为m的后缀的个数 通过在i-1添加str[i]字符转移,或者以st ...
- Codeforces 543D Road Improvement(DP)
题目链接 Solution 比较明显的树形DP模型. 首先可以先用一次DFS求出以1为根时,sum[i](以i为子树的根时,满足要求的子树的个数). 考虑将根从i变换到它的儿子j时,sum[i]产生的 ...
- Codeforces 543C Remembering Strings(DP)
题意比较麻烦 见题目链接 Solution: 非常值得注意的一点是题目给出的范围只有20,而众所周知字母表里有26个字母.于是显然对一个字母进行变换后是不影响到其它字符串的. 20的范围恰好又是常见状 ...
随机推荐
- 【Python从入门到精通】(二)怎么运行Python呢?有哪些好的开发工具(PyCharm)
您好,我是码农飞哥,感谢您阅读本文,欢迎一键三连哦. 这是Pyhon系列文章的第二篇,本文主要介绍如何运行Python程序以及安装PyCharm开发工具. 干货满满,建议收藏,需要用到时常看看. 小伙 ...
- Java版流媒体编解码和图像处理(JavaCPP+FFmpeg)
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- numpy数组的计算
1.数组的形状 查看数组的形状: import numpy as np a = np.array([[1, 2, 3, 4, 5], [5, 6, 7, 8, 9]]) print(a.shape) ...
- Python中的括号()、[]、{}
长时间不用容易混淆,仅记! 在Python语言中最常见的括号有三种,分别是:小括号().中括号[].花括号{} . Python中的小括号(): 代表tuple元祖数据类型,元祖是一种不可变序列.大多 ...
- 全志Linux Tina编译demoOmxVdec错误
测试裸流 Making install in demoOmxVdec make[6]: Entering directory '/home/liuxueneng/WorkCode/Homlet-Tin ...
- 栈的压入、弹出顺序 牛客网 剑指Offer
栈的压入.弹出顺序 牛客网 剑指Offer 题目描述 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序.假设压入栈的所有数字均不相等.例如序列1,2,3,4,5是 ...
- Ubuntu 16.04 菜单栏 换位置 挪到左边 挪到下边
Ubuntu菜单栏的位置可以调 到左侧 或者底部 调整到底部 $ gsettings set com.canonical.Unity.Launcher launcher-position Bottom ...
- hdu 5090 Game with Pearls (额,, 想法题吧 / 二分图最大匹配也可做)
题意: 给你N个数,a1,,,,an.代表第i个管子里有ai个珍珠. 规定只能往每根管里增加k的倍数个珍珠. 如果存在一套操作,操作完毕后可以得到1~N的一个排列,则Jerry赢,否则Tom赢. 问谁 ...
- Notepad++ 过滤注释行和空行
Notepad++ 删除指定字符开头的行的正则表达式 1.删除A之后的所有字符用:A.*$ 2.删除A之前的所有字符用:^([^s]*)A ####如果是其他字符就把A替换为其他字符 注释:如何是特殊 ...
- GoLang设计模式12 - 空对象模式
空对象设计模式是一种行为型设计模式,主要用于应对空对象的检查.使用这种设计模式可以避免对空对象进行检查.也就是说,在这种模式下,使用空对象不会造成异常. 空对象模式的组件包括: Entity:接口,定 ...