# Codeforces Round 1011 (Div. 2)
Codeforces Round 1011 (Div. 2)
A.Serval and String Theory
思路分析
本题可以分情况讨论。
设当前字符串为 \(s\),反转后的字符串为 \(t\)
- 字符串只有 \(1\) 种字符,那么 \(s = t\) 一定成立
- \(s \geq t\)
- \(m = 0\),那么无法交换字符使得 \(s < t\)
- \(m > 0\), 可以通过一次交换,得到 \(s < t\)
- \(s < t\) 不用任何操作,就满足 \(s < t\)
代码
#include <bits/stdc++.h>
// #define int long long
#define PII pair<int,int>
#define endl "\n"
#define LL long long
#define fi first
#define se second
#define debug(a) cout<<#a<<"="<<a<<endl;
#define all(x) (x).begin(),(x).end()
#define pb push_back
#define sz(x) (int)x.size()
#define rd(x, y) rand() % (y - x + 1) + x
#define ls u << 1
#define rs u << 1 | 1
using namespace std;
const int N = 300010;
int a[N], n, m, k;
void solve(){
cin >> n >> m;
string s, t;
cin >> s;
t = s;
map<char,int>mp;
for(auto it : s){
mp[it] ++;
}
reverse(all(t));
if(mp.size() == 1 || (!m && s >= t)) cout << "NO\n";
else cout << "YES\n";
}
signed main(){
int tt = 1;
cin >> tt;
while(tt -- ){
solve();
}
return 0;
}
B Serval and Final MEX
思路分析
考虑 \(0\) 的个数 cnt
和在字符串的存在的位置。
-
cnt = 0
,直接选择区间 \([1, n]\) -
\(a[1] = a[n] = 0\),那么依次选择区间 \([1, 2], [n - 2, n - 1], [1, 2]\)。
-
cnt = 1
,分情况讨论,设0
的下标为 \(idx\)- \(idx != 1\),选择区间\([idx - 1, idx], [1, n - 1]\)
- \(idx != n\) ,选择区间\([idx, idx + 1], [1, n - 1]\)
-
其余情况,直接把所有 \(0\) 所在区间合并一次,再合并一次整个序列即可。
代码
#include <bits/stdc++.h>
// #define int long long
#define PII pair<int,int>
#define endl "\n"
#define LL long long
#define fi first
#define se second
#define debug(a) cout<<#a<<"="<<a<<endl;
#define all(x) (x).begin(),(x).end()
#define pb push_back
#define sz(x) (int)x.size()
#define rd(x, y) rand() % (y - x + 1) + x
#define ls u << 1
#define rs u << 1 | 1
using namespace std;
const int N = 300010;
int a[N], n, m, k;
void solve(){
cin >> n;
int cnt = 0;
vector<int>q;
for(int i = 1; i <= n; i ++ ){
cin >> a[i];
if(a[i] == 0){
cnt ++;
q.pb(i);
}
}
vector<PII>ans;
if(a[1] == 0 && a[n] == 0){
ans.pb({1, 2});
ans.pb({2, n - 1});
ans.pb({1, 2});
}else{
if(cnt == 0){
ans.pb({1, n});
}else{
if(cnt == 1){
if(q[0] - 1 >= 1){
ans.pb({q[0] - 1, q[0]});
ans.pb({1, n - 1});
}else{
ans.pb({q[0], q[0] + 1});
ans.pb({1, n - 1});
}
}else{
ans.pb({q[0], q[cnt - 1]});
ans.pb({1, n - (q[cnt - 1] - q[0])});
}
}
}
cout << ans.size() << endl;
for(auto [x, y] : ans){
cout << x << " " << y << endl;
}
}
signed main(){
int tt = 1;
cin >> tt;
while(tt -- ){
solve();
}
return 0;
}
C - Serval and The Formula
思路分析
比赛的时候没有发现结论,构造做的。
性质
1.要满足题目中的式子,那么 \(x + k\) 和 \(y + k\)的二进制中的 1
的位置要错开。
2.\(x = y\),无法构造,输出 -1
。
构造
1.从低位到高位去遍历 \(x, y\)的二进制数,记录离当前这一位最近的\(x\) 和 \(y\) 在某一位二进制数不同的位置last
。
2.假设当前\(x\) 和 \(y\) 的第 \(i\) 位都是 \(1\),我们可以对 \(x\)
和 \(y\) 都加上 \(2^{last} + 2^{last + 1} +…… 2^{i - 1}\),来把 \(x\) 和 \(y\) 的第 \(i\) 位变成不一样,然后不断的更新 last
。
3.如果一开始 \(x\) 的最低位和 \(y\) 的最低位都不一样。那么就一直加 \(2^i\),直到出现第一个不一样的位置为止。
代码
#include <bits/stdc++.h>
#define int long long
#define PII pair<int,int>
#define endl "\n"
#define LL long long
#define fi first
#define se second
#define debug(a) cout<<#a<<"="<<a<<endl;
#define all(x) (x).begin(),(x).end()
#define pb push_back
#define sz(x) (int)x.size()
#define rd(x, y) rand() % (y - x + 1) + x
#define ls u << 1
#define rs u << 1 | 1
using namespace std;
const int N = 300010;
int a[N], n, m, k, qmi[70];
void solve(){
int x, y;
cin >> x >> y;
int ans = 0;
if(x == y){
cout << "-1\n";
}else{
int last = -1;
int cnt = 0;
while(x + y != (x ^ y)){
cnt ++;
for(int i = 0; i <= 60; i ++ ){
if((x >> i & 1) != (y >> i & 1)){
last = i;
}
if(x >> i & 1){
if((x >> i & 1) == (y >> i & 1)){
if(last == -1){//一开始没有找到二进制位不同的时候
ans += qmi[i];
x += qmi[i];
y += qmi[i];
break;
}else{
//从last + 到i - 1
int sum = 0;
for(int j = last; j <= i - 1; j ++ ) sum += qmi[j];
x += sum;
y += sum;
ans += sum;
break;
}
}
}
}
}
cout << ans << endl;
}
}
signed main(){
int tt = 1;
qmi[0] = 1;
for(int i = 1; i <= 60; i ++ ) qmi[i] = qmi[i - 1] * 2;
cin >> tt;
while(tt -- ){
solve();
}
return 0;
}
D - Serval and Kaitenzushi Buffet
思路分析
本题考虑最多(拿 + 吃)多少盘Buffet,很明显是\(\lfloor \frac{n} {k + 1} \rfloor\)。要想吃的最多,尽可能拿的最多,所以我们每次在每一次拿Buffet的截止时间的时候去拿之前遍历过的且没有拿的且满意度最大的即可,用一个堆或者multiset
维护一下即可。
代码
/*
拿需要1个单位时间
吃需要k个单位时间
所以一共可以拿 n / (k + 1) 次,那么贪心的在每次拿的最后截止时间去拿之前遍历过的但是没吃的即可
*/
#include <bits/stdc++.h>
#define int long long
#define PII pair<int,int>
#define endl "\n"
#define LL long long
#define fi first
#define se second
#define debug(a) cout<<#a<<"="<<a<<endl;
#define all(x) (x).begin(),(x).end()
#define pb push_back
#define sz(x) (int)x.size()
#define rd(x, y) rand() % (y - x + 1) + x
#define ls u << 1
#define rs u << 1 | 1
using namespace std;
const int N = 300010;
int a[N], n, m, k;
void solve(){
cin >> n >> k;
for(int i = 1; i <= n; i ++ ){
cin >> a[i];
}
multiset<int>s;
int ans = 0;
for(int i = 1; i <= n; i ++ ){
s.insert(a[i]);
if((n - i + 1) % (k + 1) == 0){
ans += *s.rbegin();
s.erase(s.find(*s.rbegin()));
}
}
cout << ans << endl;
}
signed main(){
int tt = 1;
cin >> tt;
while(tt -- ){
solve();
}
return 0;
}
E. Serval and Modulo
思路分析
性质
1.假设当前\(a_1 \% k = b_1, a_2 \%k = b_2…… a_n \% k = b_n\),那么有 \(a_1 - b_1, a_2 - b_2, …… a_n - b_n\) 都是 \(k\) 的倍数。
2.\(b\)数组是打乱的,所以我们只能推出另一个性质 \(\sum_{i =1}^n a_i - \sum_{i =1}^n b_i\)是 \(k\) 的倍数。
设 \(m\) = \(\sum_{i =1}^n a_i - \sum_{i =1}^n b_i\)
根据性质\(2\),我们可以很快想到去枚举 \(m\) 的因子,然后依次检查是否和法即可。
判断是否合法:用 stl
会超时,可以通过排序来依次判断每一位是否相等,因为 \(n\) 是比较小的。
特殊情况
如果\(a\)数组排好序以后 和 \(b\) 数组一样的话,直接挑一个很大的数即可。
#include <bits/stdc++.h>
#define int long long
#define PII pair<int,int>
#define endl "\n"
#define LL long long
#define fi first
#define se second
#define debug(a) cout<<#a<<"="<<a<<endl;
#define all(x) (x).begin(),(x).end()
#define pb push_back
#define sz(x) (int)x.size()
#define rd(x, y) rand() % (y - x + 1) + x
#define ls u << 1
#define rs u << 1 | 1
using namespace std;
const int N = 300010;
int a[N], n, m, k, b[N], c[N];
bool check(int x){
for(int i = 1; i <= n; i ++ ){
c[i] = a[i] % x;
}
sort(c + 1, c + 1 + n);
for(int i = 1; i <= n; i ++ ){
if(c[i] != b[i]) return 0;
}
return 1;
}
void solve(){
cin >> n;
mp.clear();
for(int i = 1; i <= n; i ++ ) cin >> a[i];
for(int i = 1; i <= n; i ++ ){
cin >> b[i];
mp[b[i]] ++;
}
sort(a + 1, a + 1 + n);
sort(b + 1, b + 1 + n);
bool f = 0;
for(int i = 1; i <= n; i ++ ){
if(a[i] != b[i]){
f = 1;
break;
}
}
if(!f){
cout << 1919810 << endl;
return;
}
int sum = 0;
for(int i = 1; i <= n; i ++ ){
sum += a[i] - b[i];
}
k = -1;
for(int i = 1; i * i <= sum; i ++ ){//1e5
if(sum % i == 0){
if(check(i)){
k = i;
break;
}
if(sum / i <= 1e9 && check(sum / i)){
k = sum / i;
break;
}
}
}
cout << k << endl;
}
signed main(){
int tt = 1;
cin >> tt;
while(tt -- ){
solve();
}
return 0;
}