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的范围恰好又是常见状 ...
随机推荐
- UltraSoft - Alpha - Scrum Meeting 8
Date: Apr 23th, 2020. Scrum 情况汇报 进度情况 组员 负责 昨日进度 后两日任务 CookieLau PM.后端 aliyun连接前后端,跑通demo 实现邮箱注册的验证码 ...
- RabbitMQ处理未被路由的消息
我们经常使用消息队列进行系统之间的解耦,日志记录等等.但是有时候我们在使用 RabbitMQ时,由于exchange.bindKey.routingKey没有设置正确,导致我们发送给交换器(excha ...
- 笔记本加装SSD并装系统
1,首先了解笔记本配置信息 一般加装SSD都是120~256左右,并使用原有的机械硬盘:首先确定加装位置:1,是否支持M.2接口:假如支持,可以直接购买,拆机装上:我的笔记本不支持:所以考虑2,光驱的 ...
- Java安全之Thymeleaf SSTI分析
Java安全之Thymeleaf SSTI分析 写在前面 文章首发:https://www.anquanke.com/post/id/254519 最近看了一遍Thymeleaf,借此机会学习一下Th ...
- 攻防世界 WEB 高手进阶区 easytornado Writeup
攻防世界 WEB 高手进阶区 easytornado Writeup 题目介绍 题目考点 Python模板 tornado 模板注入 Writeup 进入题目, 目录遍历得到 /flag.txt /w ...
- idea如何在终端使用git并解决终端中文乱码
idea使用git终端 在idea设置中 找到Settings-Tools-Terminal-Shell path,替换为git安装目录下的bin/bash.exe 解决中文乱码 在git安装目录下找 ...
- Spring Cloud调用接口过程
Spring Cloud 在接口调用上,大致会经过如下几个组件配合: Feign== >Hystrix ==>Ribbon ==>Http Client(apache http co ...
- js深拷贝你还不会吗
js深拷贝 在讲正题之前我们要先了解数据存储的方式 数据存储方式 在讲之前我们要先知道值类型和引用类型的存储方式. 在JavaScript数据类型中有两种数据类型. 值类型:字符串(String).数 ...
- oracle的 listagg() WITHIN GROUP () 行转列函数的使用
1.使用条件查询 查询部门为20的员工列表 -- 查询部门为20的员工列表 SELECT t.DEPTNO,t.ENAME FROM SCOTT.EMP t where t.DEPTNO ...
- VLAN实验
VLAN实验 如图所示:图中共有四个广播域,左边逻辑的分为两个广播域,右边也是逻辑的分为两个广播域, 配置顺序先配置交换机,在配置路由器 SW1 配置: 1.首先创建vlan [sw1]vlan ba ...