Codeforces Round 958 (Div. 2)

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;
}

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值