A题:Split the Multiset
题意:
给你一个正整数n,你可以进行多次分裂(比如将5可以拆成1+4或者2+3),问最后变成n个1的最小操作次数是多少?
思路:
分裂是总和不会变的,任何一种分裂法的操作次数都是一样的。
我想到的是,每次可以分裂成k个,那么至多可以分裂出k - 1个1,这就是贪心的最优方案。
对于一个数n,要分裂出n - 1个1,每次可以分裂k - 1个,要向上取整
a / b 的向上取整 (a + b - 1) / b
code:
inline void solve() {
int n, k; cin >> n >> k;
cout << (n - 1 + (k - 1) - 1) / (k - 1) << endl;
return;
}
B题:Make Majority
题意:
给你一个01串,每次你可以选定一个区间[l,r],将这个区间改成区间中的多数(比如这个区间0比1多就改成0,反之则为1),问是否可能将整个01串全变成1?
思路:
毫无疑问的,我们可以先把连续的0全都变成一个0
当前的字符串是01交错的,中间的0都可以通过101这种方式全都变成1
所以最后会剩下
01...10
01...1
1...1
1...10
这几种情况,那只要比较1的数量和0的数量就可以了。
然后我们就可以考虑优化,记原来的字符串(连续0都改成单个0)中0的数量为cnt0,1的数量为cnt1
既然中间一定都能变成1,相当于cnt0 - 1, cnt1 - 1
两者之间的大小关系不会受到影响,所以只要比较0的数量和1的数量就行了。
code:
inline void solve() {
int n; cin >> n;
string s; cin >> s;
s = " " + s + " ";
int c0 = 0, c1 = 0;
for (int i = 1; i <= n; i ++ ) {
if (s[i] - '0') c1 += 1;
else {
while (s[i] == '0') i += 1;
i -= 1;
c0 += 1;
}
}
cout << (c1 > c0 ? "Yes\n" : "No\n");
return;
}
C题:Increasing Sequence with Fixed OR
题意:
构造一个数组a,使得ai | ai-1 = n,并且数组大小要求最大
思路:
我们最后肯定是要将n放进去的,我们正着想比较麻烦,那就反着想,构造一个从大到小递减的数组。
那这样的话,我们只要每次加入的元素尽可能的大就行了。
一开始我们放入n,
接着我们应该放入啥呢?
放入去掉二进制第一个1的n,这样是次大的。
接着次次大的呢?
同理,放入去掉二进制第二个1的n,这样是次次大的,并且保证了或起来是n
...
就这样进行下去即可。
code:
inline void solve() {
ll n; cin >> n;
vector<ll> ans;
ans.push_back(n);
for (int i = 0; i <= 60; i ++ ) {
if (n & (1ll << i) && n != (1ll << i)) ans.push_back(n ^ (1ll << i));
}
reverse(ans.begin(), ans.end());
cout << ans.size() << endl;
for (ll x : ans) cout << x << ' ';
cout << endl;
return;
}