传送门
题意
有n个数,你可以对每个数进行两种操作:
将一个数乘以2
将一个数变为其二分之一并向下取整
问最少通过多少次操作可以将这些数的值变为全部相同。
分析
好像很多题解都是写的暴力啊,我来补一个换根
D
P
DP
DP
考虑建树,
i
i
i和
i
∗
2
i * 2
i∗2之间连一条单向边,
i
/
2
i / 2
i/2和
i
i
i之间连一条单向边,然后如果要把所有的数全部转换成1的话,就是记录每一个点的代表的数出现的次数乘以这个点的深度的和,然后要统计每一个点作为答案的情况,换一下根即可
代码
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define debug(x) cout<<#x<<":"<<x<<endl;
#define dl(x) printf("%lld\n",x);
#define di(x) printf("%d\n",x);
#define _CRT_SECURE_NO_WARNINGS
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
typedef vector<int> VI;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int N = 2e5 + 10,M = N * 2;
const ll mod = 1000000007;
const double eps = 1e-9;
const double PI = acos(-1);
template<typename T>inline void read(T &a) {
char c = getchar(); T x = 0, f = 1; while (!isdigit(c)) {if (c == '-')f = -1; c = getchar();}
while (isdigit(c)) {x = (x << 1) + (x << 3) + c - '0'; c = getchar();} a = f * x;
}
int gcd(int a, int b) {return (b > 0) ? gcd(b, a % b) : a;}
int h[N],e[M],ne[M],idx;
ll w[N],dep[N];
ll ans[N];
ll sz[N];
int n;
void add(int x,int y){
ne[idx] = h[x],e[idx] = y,h[x] = idx++;
}
void dfs(int u,int fa){
sz[u] = w[u];
for(int i = h[u];~i;i = ne[i]){
int j = e[i];
dep[j] = dep[u] + 1;
dfs(j,u);
sz[u] += sz[j];
}
}
void dfs_(int u,int fa){
for(int i = h[u];~i;i = ne[i]){
int j = e[i];
if(j % 2 == 1 && sz[j] != n) ans[j] = INF;
else ans[j] = ans[u] - sz[j] + (n - sz[j]);
dfs_(j,u);
}
}
int main() {
read(n);
memset(h,-1,sizeof h);
for(int i = 1;i <= n;i++) {
int x;
read(x);
w[x]++;
}
for(int i = 1;i <= 100000;i++){
if(i * 2 <= 100000) add(i,i * 2);
if(i / 2 > 0 && i % 2 == 1) add(i / 2,i);
}
dfs(1,0);
for(int i = 2;i <= 100000;i++) {
ans[1] += dep[i] * w[i];
}
dfs_(1,0);
ll res = INF;
for(int i = 1;i <= 100000;i++) res = min(res,ans[i]);
dl(res);
return 0;
}