C. Jellyfish and Green Apple
题意
有 n n n 个均为 1 k g 1kg 1kg 的苹果,要平均分给 m m m 个人,每次操作可以将一块苹果平分成两份。问最少需要多少次操作才能平均分配?(或者 − 1 -1 −1表示无法平均分配)
思路
对于
n
>
m
n>m
n>m 的时候,我们只需要给每个人都分一个苹果,直到
n
<
m
n<m
n<m。所以我们只需考虑
n
<
m
n<m
n<m 的情况
每个人可以分到
n
m
\frac{n}{m}
mn 的苹果,我们先将其约分成最简分数的形式,也就是分子分母同时除
g
c
d
(
n
,
m
)
gcd(n,m)
gcd(n,m),此时如果发现分母
m
g
c
d
(
n
,
m
)
\dfrac {m}{gcd(n,m)}
gcd(n,m)m 不是
2
2
2 的整数次幂,那么答案很显然就是
−
1
-1
−1。否则我们可以使用
n
g
c
d
(
n
,
m
)
\dfrac {n}{gcd(n,m)}
gcd(n,m)n 个
m
g
c
d
(
n
,
m
)
\dfrac {m}{gcd(n,m)}
gcd(n,m)m 来构成小数部分。
判断一个数是否是 2 2 2 的整数次幂可以使用:__builtin_popcount()
可以注意到,每切一次,苹果块的数量就会增加 1 1 1。所以切的次数最少 ⇔ \Leftrightarrow ⇔ 最后苹果块的数量最少。
可以证明对于 n g c d ( n , m ) \dfrac {n}{gcd(n,m)} gcd(n,m)n 个 m g c d ( n , m ) \dfrac {m}{gcd(n,m)} gcd(n,m)m,一定是以 n g c d ( n , m ) \dfrac {n}{gcd(n,m)} gcd(n,m)n 的二进制形式来构成是最优的。比如 n = 3 , m = 4 n = 3,m = 4 n=3,m=4, n = [ 11 ] 2 n = [11]_2\quad n=[11]2。那么一定是 2 个 0.25 = 0.5 2 个 0.25 = 0.5\quad 2个0.25=0.5 + 1 个 0.25 \quad1 个 0.25 1个0.25 来构成小数部分是最优的。
那么最后除了一开始的不用切割的整个完整苹果的分配,小数部分最后每个人都有若干个苹果块的话,减去一开始用来切割成小数部分的 n n n 个,就是增加的苹果块数量,也即是切割数字了。
// Problem: C. Jellyfish and Green Apple
// Contest: Codeforces - Codeforces Round 901 (Div. 2)
// URL: https://codeforces.com/contest/1875/problem/C
// Memory Limit: 256 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
#define fore(i,l,r) for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
const int INF=0x3f3f3f3f;
const long long INFLL=0x3f3f3f3f3f3f3f3fLL;
typedef long long ll;
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int t;
std::cin>>t;
while(t--){
int n,m;
std::cin>>n>>m;
n %= m;
int a = n / std::__gcd(n,m);
int b = m / std::__gcd(n,m);
if(__builtin_popcount(b) > 1) std::cout<<-1<<endl;
else std::cout<<1ll * __builtin_popcount(a)*m - n<<endl;
}
return 0;
}
D. Jellyfish and Mex
题意
有一个长度为
n
n
n 的非负整数数组
a
a
a,
m
m
m 初始为
0
0
0
做下列操作
n
n
n 次:
- 删除 a a a 中的一个元素
- 将 M E X ( a ) MEX(a) MEX(a) 加到 m m m 上
求最后 m m m 的最小值
思路
任何长度为 n n n 的非负整数数组 a a a,其 M E X ( a ) MEX(a) MEX(a) 都 ≤ n \leq n ≤n
对于当前的 M E X ( a ) MEX(a) MEX(a),我们一定是选择一个小于 M E X ( a ) MEX(a) MEX(a) 的 x x x 来删除,否则就是无用功。一旦选择了一个 x x x ,剩下的操作就一定是优先将 x x x 全部删除,从而将 M E X ( a ) MEX(a) MEX(a) 更新为 x x x。对于那些大于 x x x 的元素,我们可以留到 M E X ( a ) MEX(a) MEX(a) 变为 0 0 0 后再删除。那么现在要考虑的就是 x x x 的选择顺序问题,可以肯定的是: x x x 的选择一定是递减的。
考虑 D P DP DP:定义 d p [ i ] dp[i] dp[i] 代表在 M E X ( a ) = i MEX(a) = i MEX(a)=i 并且还没有删除任何 x < i x < i x<i 的元素的条件下,表示的 m m m 的最小值。那么转移方程为:对于某个值 j j j, ∀ i > j , d p [ j ] = m i n ( d p [ j ] , d p [ i ] + i ⋅ ( c n t [ j ] − 1 ) + j ) \forall i>j,dp[j] = min(dp[j],dp[i] +i\cdot(cnt[j]-1) + j) ∀i>j,dp[j]=min(dp[j],dp[i]+i⋅(cnt[j]−1)+j),意思就是将 c n t [ j ] cnt[j] cnt[j] 个 j j j 全部删除,除了最后一次删除后 M E X ( a ) MEX(a) MEX(a) 变为 j j j,前面全部的操作 M E X ( a ) MEX(a) MEX(a) 都是 i i i
// Problem: D. Jellyfish and Mex
// Contest: Codeforces - Codeforces Round 901 (Div. 2)
// URL: https://codeforces.com/contest/1875/problem/D
// Memory Limit: 256 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
#define fore(i,l,r) for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
const int INF=0x3f3f3f3f;
const long long INFLL=0x3f3f3f3f3f3f3f3fLL;
typedef long long ll;
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int t;
std::cin>>t;
while(t--){
int n;
std::cin>>n;
std::vector<ll> cnt(n+5,0);
int x;
fore(i,1,n+1){
std::cin>>x;
if(x <= n) ++cnt[x];
}
int mex = 0;
while(cnt[mex]) ++mex;
std::vector<ll> dp(n+5,INFLL);
dp[mex] = 0;
for(int i=mex;i>=1;--i)
for(int j=0;j<i;++j)
dp[j] = std::min(dp[j],dp[i] + 1ll*i*(cnt[j]-1) + j);
std::cout<<dp[0]<<endl;
}
return 0;
}