Description
有 个大小为
的连通块,规定合并连通块的代价为 连通块的大小之差,求将这
个联通块合并为
个连通块所需的最小代价。
Solution
思路:
显然,当连通块大小相等时合并连通块的代价为 0。
所以我们可以通过分治的思路去解决合并大小为 (n/2) 和 (n - n/2) 最小代价。
对于每个大小为 k 的连通块,只有当 k 为奇数时才有可能产生贡献
当 k 为奇数时,产生的贡献为合并 (k/2 + 1) 的代价 + 合并 (k / 2)的代价 + 1[(k / 2 + 1) - (k / 2)]
所以我们可以通过递归解决这个问题,递归边界是 k == 1。
Tips: 注意到递归函数会多次询问,有可能导致超时,使用 map 记录合并大小为 k 的连通块的最小代价剪枝。
code:
#include <bits/stdc++.h>
#define int long long int
#define endl '\n'
#define pb push_back
using namespace std;
const int N = 1E6 + 70,mod = 1e9 + 7;
map<int,int> mp;
inline int read(){
char ch = getchar();
int x =0,f =1;
for(;!isdigit(ch);ch = getchar())
if(ch == '-') f = -1;
for(; isdigit(ch);ch = getchar())
x = (x << 1) + (x << 3) + ch - '0';
return f * x;
}
int get(int x){
if(mp[x]) return mp[x];
if(x == 1) return 0;
return mp[x] = (get(x /2 + (x & 1)) + get(x /2) + (x & 1));
}
void slove(){
int n = read();
cout << get(n) << endl;
}
signed main(){
int T = 1;
T = read();
while(T --){
slove();
}
return 0;
}