# E-Making Anti-Palindromes

Problem - 1822E - Codeforces

判断能否通过交换实现 s[i]!=s[ni+1]s[i] != s[n - i + 1]

  • 计算每一个字符出现的次数,如果某个字符出现次数超过一半,就不可能实现;否则就可以实现。

如果可以实现,最少的交换次数是多少:

  • 计算出现 s[i]==s[ni+1]s[i] == s[n - i + 1] 的总对数,以及对于每个字母出现一对的个数
  • 其中对于各个字母,出现对数最大的就是最后的最少交换次数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <bits/stdc++.h>
#define int long long
using namespace std;
void solve()
{
int n; cin >> n;
string s; cin >> s;
if(n % 2)
{
cout << -1 << '\n';
return;
}
vector<int> cnt(26);
for(int i = 0; i < s.size(); i++) cnt[s[i] - 'a']++;
for(int i = 0; i < 26; i++)
if(cnt[i] * 2 > n)
{
cout << -1 << '\n';
return;
}
int i = 0, j = s.size() - 1;
int pair = 0;
vector<int> cnt_pair(26);
while(i < j)
{
if(s[i] == s[j])
{
pair++;
cnt_pair[s[i] - 'a']++;
}
i++, j--;
}
for(int i = 0; i < 26; i++)
{
if(cnt_pair[i] * 2 > pair)
{
cout << cnt_pair[i] << '\n';
return;
}
}
cout << (pair + 1) / 2 << '\n';
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int t;
cin >> t;
while(t--)
{
solve();
}
return 0;
}

# G - 国王游戏

国王游戏 - 洛谷

高精 + 贪心

出自 TainityAnle 的洛谷题解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#include <bits/stdc++.h>
#define int long long
using namespace std;
class bigint
{
private:
string value;
int cmp(const bigint & other) const
{
if(value.length() != other.value.length()) return value.length() < other.value.length() ? -1 : 1;
return value.compare(other.value);
}
public:
bigint(string s = "0")
{
value = s;
}
static bool smaller(const bigint &a, const bigint &b)
{
if(a.cmp(b) > 0) return false;
return true;
}
bigint operator+(const bigint& other)
{
int carry = 0;
int i = value.length() - 1, j = other.value.length() - 1;
string result;
while(i >= 0 || j >= 0 || carry > 0)
{
int sum = carry;
if(i >= 0) sum += value[i--] - '0';
if(j >= 0) sum += other.value[j--] - '0';
result.push_back(sum % 10 + '0');
carry = sum / 10;
}
reverse(result.begin(), result.end());
result.erase(0, result.find_first_not_of('0'));
if(result.empty()) return bigint("0");
return bigint(result);
}
bigint operator-(const bigint & other)
{
int borrow = 0;
string result;
int i = value.length() - 1, j = other.value.length() - 1;
while(i >= 0 || j >= 0)
{
int diff = borrow;
if(i >= 0) diff += value[i--] - '0';
if(j >= 0) diff -= other.value[j--] - '0';
if(diff < 0)
{
diff += 10;
borrow = -1;
}
else borrow = 0;
result.push_back(diff % 10 + '0');
}
reverse(result.begin(), result.end());
result.erase(0, result.find_first_not_of('0'));
if(result.empty()) return bigint("0");
return bigint(result);
}
bigint operator*(const bigint & other)
{
string result(value.length() + other.value.length(), '0');
for(int i = value.length() - 1; i >= 0; i--)
{
int carry = 0;
for(int j = other.value.length() - 1; j >= 0; j--)
{
int sum = carry + (result[i + j + 1] - '0') + (value[i] - '0') * (other.value[j] - '0');
result[i + j + 1] = sum % 10 + '0';
carry = sum / 10;
}
result[i] += carry;
}
result.erase(0, result.find_first_not_of('0'));
if(result.empty()) return bigint("0");
return bigint(result);
}
bigint operator/(const bigint& other)
{
if(other.value == "0")
{
throw runtime_error("Division by zero");
}
if(cmp(other) < 0)
{
return bigint("0");
}
string result;
bigint temp("0");
for(auto digit: value)
{
temp = temp * bigint("10") + bigint(string(1, digit));
int count = 0;
while(temp.cmp(other) >= 0)
{
temp = temp - other;
count ++;
}
result.push_back(count + '0');
}
result.erase(0, result.find_first_not_of('0'));
if(result.empty()) return bigint("0");
return bigint(result);
}
friend ostream& operator<<(ostream& os, const bigint& num)
{
os << num.value;
return os;
}
};
bool cmp(pair<int, int> a, pair<int, int> b)
{
return a.first * a.second < b.first * b.second;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int n; cin >> n;
n = n + 1;
vector<pair<int, int>> v(n);
for(int i = 0; i < n; i++) cin >> v[i].first >> v[i].second;
sort(v.begin() + 1, v.end(), cmp);
//for(int i = 0; i < n; i++) cout << v[i].first << " " << v[i].second <<'\n';
bigint ans(to_string(v[0].first)), res("0");
for(int i = 1; i < n; i++)
{
bigint tmp = ans / bigint(to_string(v[i].second));
ans = ans * bigint(to_string(v[i].first));
if(bigint::smaller(res, tmp)) res = tmp;
}
cout << res;
return 0;
}

# H - 字符串排序

P8719 字串排序 - 洛谷

字串排序 - 洛谷题解

题目描述:要求求一个逆序对个数为 vv 的字典序最小的最短的字符串

解题思路

第一步:求出满足字典序为 vv 的最短的字符串的长度

  1. 对于一个字符串,如果它的长度 lenlen 小于等于 26,那么它能产生的最多逆序对的个数为 len * (len - 1) / 2

  2. 对于一个字符串,如果它的长度 lenlen 大于 26, 那么一定会有重复的字符串,因为要求字典序最小,应该让字典序小的出现次数多

    找规律可知:

    • 若原字符串是 cbbaa ,那么插入 cc 会使字符串逆序对最多
    • 若原字符串是 ccbba ,那么插入 aa 会使字符串逆序对最多

    即需要让不同字符的个数相差最小,这时逆序对最多。

  3. 对于长度为 lenlen 的字符串,计算逆序对个数的最大值:

    • 对于重复次数更多的字符出现的次数len / 26 + 1 ,对于每个字符产生的逆序对个数是: len - (len / 26 + 1) (去除自己的个数),重复次数更多的字符的个数len % 26
    • 对于重复次数少的字符出现的次数len / 26 ,对于每个字符产生的逆序对个数是: len - (len / 26) ,重复次数少的字符的个数26 - len % 26

    总逆序对个数是: int L= ((len - (len / 26 + 1)) * (len / 26 + 1) * (len % 26) + (len - (len / 26)) * (len / 26) * (26 - len % 26)) >> 1 ,记得最后要 / 2 因为所有的逆序对都算了两遍

第二步:往长度为 L 的字符串的填入字符

一位一位地遍历,因为要求字典序最小,所以需要从小往大遍历,看是否能放。

判断的依据是:如果这个节点前面的逆序对个数 + 加上这个节点后新产生的逆序对个数 + 这个节点之后能产生的最大的逆序对个数 >= v,那么这个节点是满足条件的。(“这个节点之后能产生的最大的逆序对个数” 没弄太明白)