Codeforces Round 855 (Div. 3) A-G完全题解
A
没啥好说的。(其实比赛时写丑了。把字母全部统一成小写更好,因为大小写不影响)
#include <bits/stdc++.h>
using namespace std;
#define N 100010
#define ll long long
template <class T>
inline void read(T& a){
T x = 0, s = 1;
char c = getchar();
while(!isdigit(c)){ if(c == '-') s = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + (c ^ '0'); c = getchar(); }
a = x * s;
return ;
}
bool judge(char s, int x){
if(s == x || s == x - 'A' + 'a') return 1;
else return 0;
}
char s[10001];
int main(){
//freopen("hh.txt", "r", stdin);
int T; read(T);
while(T--){
int n; read(n);
cin >> s;
int len = strlen(s);
bool flag = 1;
int id = 0;
bool vis[4];
memset(vis, 0, sizeof(vis));
while(s[id] == 'm' || s[id] == 'M' && id < len) id++, vis[0] = 1;
if(s[id] != 'e' && s[id] != 'E') flag = 0;
while(s[id] == 'e' || s[id] == 'E' && id < len) id++, vis[1] = 1;
if(s[id] != 'o' && s[id] != 'O') flag = 0;
while(s[id] == 'o' || s[id] == 'O') id++, vis[2] = 1;
if(s[id] != 'w' && s[id] != 'W') flag = 0;
while(s[id] == 'w' || s[id] == 'W') id++, vis[3] = 1;
if(id <= len - 1) flag = 0;
for(int i = 0; i < 4; i++) flag &= vis[i];
if(!flag) puts("NO");
else puts("YES");
}
return 0;
}
B
用两个数组记录大小写数量即可。
#include <bits/stdc++.h>
using namespace std;
#define N 100010
#define ll long long
template <class T>
inline void read(T& a){
T x = 0, s = 1;
char c = getchar();
while(!isdigit(c)){ if(c == '-') s = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + (c ^ '0'); c = getchar(); }
a = x * s;
return ;
}
map <int, int> g;
int main(){
//freopen("hh.txt", "r", stdin);
int T; read(T);
while(T--){
g.clear();
int n, k;
read(n); read(k);
string s; cin >> s;
for(int i = 0; i < s.length(); i++){
g[s[i]]++;
}
int ans = 0;
for(int i = 'a'; i <= 'z'; i++){
int x = i - 'a' + 'A';
int minn = min(g[x], g[i]);
ans += minn;
g[x] -= minn, g[i] -= minn;
int maxn = max(g[x], g[i]);
int num = maxn / 2;
if(k >= num){
ans += num;
k -= num;
}
else{
ans += k;
k = 0;
}
}
cout << ans << endl;
}
return 0;
}
C (\(easy & hard\))
显然,每次遇到 \(0\), 我们都可以优先去取前面最大的值。所以用堆维护一下即可。
#include <bits/stdc++.h>
using namespace std;
#define N 100010
#define ll long long
template <class T>
inline void read(T& a){
T x = 0, s = 1;
char c = getchar();
while(!isdigit(c)){ if(c == '-') s = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + (c ^ '0'); c = getchar(); }
a = x * s;
return ;
}
priority_queue <int> q;
int main(){
//freopen("hh.txt", "r", stdin);
int T; read(T);
while(T--){
while(!q.empty()) q.pop();
int n;
read(n);
ll ans = 0;
for(int i = 1; i <= n; i++){
int x; read(x);
if(x != 0) q.push(x);
else{
if(q.empty()) continue;
else{
ans += q.top();
// printf("x: %d top: %d\n", x, q.top());
q.pop();
}
}
}
cout << ans << endl;
}
return 0;
}
D.
直接分类讨论。一长串完全相同的肯定只能算一个。两个不同的字母交界处也算一个。但是形如 \(aba\) 这种的又要减去一个,算重复了。
#include <bits/stdc++.h>
using namespace std;
#define N 100010
#define ll long long
template <class T>
inline void read(T& a){
T x = 0, s = 1;
char c = getchar();
while(!isdigit(c)){ if(c == '-') s = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + (c ^ '0'); c = getchar(); }
a = x * s;
return ;
}
int main(){
//freopen("hh.txt", "r", stdin);
int T; read(T);
while(T--){
int n; read(n);
string s; cin >> s;
int ans = 0;
for(int i = 1; i < s.length(); i++){
if(s[i] != s[i-1]) ans++;
if(s[i] == s[i-1] && s[i] != s[i+1]) ans++;
if(s[i] == s[i-1] && s[i] == s[i+1]) continue;
}
for(int i = 0; i < s.length() - 2; i++){
if(s[i] != s[i+1] && s[i] == s[i + 2]) ans--;
}
cout << ans << endl;
}
return 0;
}
E (easy & hard)
手玩一下可以知道,对于那些可以移动的字母,他们是可以任意移动的。所以不妨把原串分为三部分: \([可动] [不可动] [可动]\)。 其中左右两侧的可动区域字母可以在两个总区域内随意移动。不可动区域则直接按原串比较即可。
#include<bits/stdc++.h>
using namespace std ;
#define maxn 400100
#define int long long
int read(){
int ans = 0 , f = 1 ; char ch = getchar() ;
while ( !isdigit(ch) ){ if( ch == '-' ) f = -1 ; ch = getchar() ; }
while ( isdigit(ch) ) ans = (ans * 10) + (ch ^ '0') , ch = getchar() ;
return ans * f ;
}
int t ;
char in1[maxn] , in2[maxn] ;
int n , k ;
int cnt1[30] , cnt2[30] ;
signed main(){
t = read() ;
while(t--){
n = read() , k = read() ;
memset(cnt1 , 0 , sizeof(cnt1)) ;
memset(cnt2 , 0 , sizeof(cnt2)) ;
scanf("%s" , in1 + 1) ;
scanf("%s" , in2 + 1) ;
if(k * 2 <= n ){
for(int i = 1 ; i <= n ; i++)
cnt1[in1[i] -'a'+ 1]++ ;
for(int i = 1 ; i <= n ; i++)
cnt2[in2[i] -'a'+ 1]++ ;
bool flag = 0 ;
for(int i = 1 ; i <= 26 ; i++)
if(cnt1[i] != cnt2[i]) flag = 1 ;
if(flag == 1){
printf("no\n") ;
}
else printf("yes\n") ;
}
else if(k >= n){
bool flag = 0 ;
for(int i = 1 ; i <= n ; i++)
if(in1[i] != in2[i] )
flag = 1 ;
if(flag == 1){
printf("no\n") ;
}
else printf("yes\n") ;
}
else if(k * 2 > n){
for(int i = 1 ; i <= n - k ; i++)
cnt1[in1[i] -'a'+ 1]++ ,cnt2[in2[i] -'a'+ 1]++ ;
for(int i = k + 1 ; i <= n ; i++)
cnt1[in1[i] -'a'+ 1]++ ,cnt2[in2[i] -'a'+ 1]++ ;
bool flag = 0 ;
for(int i = 1 ; i <= 26 ; i++)
if(cnt1[i] != cnt2[i]) flag = 1 ;
for(int i = n - k + 1 ; i <= k ; i++)
if(in1[i] != in2[i])
flag = 1 ;
if(flag == 1){
printf("no\n") ;
}
else printf("yes\n") ;
}
}
return 0 ;
}
F
发现结果只跟奇偶性以及是否存在有关,与具体数量无关。所以考虑先把所有串化成 \(01\) 串。(\(0\) 表示没有或偶数个,\(1\) 表示奇数个)。然后,考虑枚举某一个字母被消去,则我们可以提前得到最终目标形态:一个有 \(25\) 个 \(1\) 和 \(1\) 个 \(0\) 的 \(01\) 串。
这样,我们枚举每个串,确认其不含有某个字母时,将目标状态与其异或一下,便能得到对应符合要求的串。
#include <bits/stdc++.h>
using namespace std;
#define N 200010
#define ll long long
template <class T>
inline void read(T& a){
T x = 0, s = 1;
char c = getchar();
while(!isdigit(c)){ if(c == '-') s = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + (c ^ '0'); c = getchar(); }
a = x * s;
return ;
}
int n;
char s[5000010];
unordered_map <int, int> g[27]; // 某个状态在缺失某个字母下对应的个数
int target[27]; // 某个字母不出现的情况下,我们的目标状态
int main(){
// freopen("hh.txt", "r", stdin);
for(int i = 0; i < 26; i++){
target[i] = ((1 << 26) - 1) ^ (1 << i);
}
ll ans = 0;
read(n);
for(int i = 1; i <= n; i++){
scanf("%s", s);
int num[27] = {0};
int len = strlen(s);
for(int j = 0; j < len; j++){
num[s[j] - 'a']++;
}
int now = 0;
for(int j = 'a'; j <= 'z'; j++){
if(num[j - 'a'] % 2 == 1){
now |= (1 << (j - 'a'));
}
}
for(int j = 'a'; j <= 'z'; j++){ // 二者都缺失这个字母的情况下
if(num[j - 'a'] == 0){
ans += g[j - 'a'][target[j - 'a'] ^ now];
}
}
for(int j = 'a'; j <= 'z'; j++){
if(num[j - 'a'] == 0)
g[j - 'a'][now]++;
}
}
cout << ans << endl;
return 0;
}
G
数哈希模板题。代码用了比较方便的 \(unsigned\) 自然溢出。
值得一提的是 \(check\) 函数,分类讨论奇偶。注意到可能会有多个奇数点,因此采用可以嵌套的递归方式最为方便。
#include <bits/stdc++.h>
using namespace std;
#define N 200010
#define ll long long
template <class T>
inline void read(T& a){
T x = 0, s = 1;
char c = getchar();
while(!isdigit(c)){ if(c == '-') s = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + (c ^ '0'); c = getchar(); }
a = x * s;
return ;
}
vector <int> G[N];
int n;
int siz[N];
vector <unsigned int> hah; // 哈希值
const unsigned int seed = (chrono::steady_clock::now().time_since_epoch().count());
unsigned int shift(unsigned int x){ // 自然溢出
x ^= seed;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
x ^= seed;
return x;
}
int fa[N];
void dfs(int u, int father){
fa[u] = father;
siz[u] = 1;
hah[u] = 1;
for(auto v : G[u]){
if(v != father){
dfs(v, u);
hah[u] += shift(hah[v]);
}
}
return ;
}
bool check(int u){
map <int, int> g;
for(auto v : G[u]){
if(v == fa[u]) continue;
g[hah[v]]++;
}
bool flag = 1;
int Size = 0;
if(u == 1) Size = G[u].size();
else Size = G[u].size() - 1; // 剪掉父亲, 获取子节点数量
if(Size % 2 == 0){
for(auto v : G[u]){
if(v == fa[u]) continue;
if(g[hah[v]] % 2 == 1) flag = 0; // 偶数情况下存在落单值
}
}
else{
int num = 0;
int id = 0;
map <int, int> vis;
for(auto v : G[u]){
if(v == fa[u]) continue;
if(g[hah[v]] % 2 == 1 && !vis[hah[v]]){ // 这里需要统计有多少个奇数,所以用 vis 去重
num++;
id = v;
vis[hah[v]] = 1;
}
}
if(num > 1) flag = 0; // 大于 1 个单配的
else{
flag &= check(id); // 检查下一层
}
}
return flag;
}
void clean(){
for(int i = 1; i <= n; i++)
G[i].clear();
hah.clear();
return ;
}
int main(){
//freopen("hh.txt", "r", stdin);
int T; read(T);
while(T--){
read(n);
for(int i = 1; i < n; i++){
int u, v;
read(u), read(v);
G[u].push_back(v);
G[v].push_back(u);
}
for(int i = 1; i <= n + 100; i++)
hah.push_back(0);
dfs(1, 0);
bool flag = check(1);
if(flag) puts("Yes");
else puts("No");
clean();
}
return 0;
}