Educational Codeforces Round 161 (Div. 2)
A - Tricky Template
难度: ⭐⭐
题目大意
给定三个字符串a, b, c; 现在有一个模板串t, 如果字符串s和t匹配需要满足两个条件;
1- 如果 ti 是小写字母, 那么 si 和 ti 相同;
2- 如果 ti 是大写字母, 那么 si 不能 ti 的小写形式相同;
问是否存在一个模板串t使得a, b都和t匹配, 而c不匹配;
解题思路
本题要从a, b串下手, 根据a, b当前字符的形式来判断 ti 的类型, 然后再用 ti 去约束c;
当ai == bi时, 如果ai是小写, 那么此时 ti 大小写都可以, 如果是小写, 只要ci不等于ai就匹配失败; 如果是大写, 那么ti的小写形式肯定不是ai, 所以还是只要ci不等于ai就匹配失败; 如果ai是大写, 那么此时ti只能是大写, 并且只要此时ci是小写就匹配失败;
当ai != bi时, 那么此时ti只能是大写, 么ti的小写形式肯定不是ai 或者bi, 所以还是只要ci不等于ai和bi就匹配失败;
神秘代码
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define endl '\n'
using namespace std;
const int N = 1e6 + 10, mod = 1e9 + 7;
typedef pair<int, int> PII;
int n, m;
signed main() {
int t;
cin >> t;
while(t--){
cin >> n;
string a, b, c;
cin >> a >> b >> c;
bool f = false;
for(int i = 0; i < n; i++){
if(a[i] == b[i] && islower(a[i])){
if(c[i] != a[i]){
f = true;
break;
}
}
else if(a[i] != b[i]){
if(islower(c[i]) && c[i] != a[i] && c[i] != b[i]){
f = true;
break;
}
}
}
if(f) cout << "YES" << endl;
else cout << "NO" << endl;
}
return 0;
}
B - A Balanced Problemset?
难度: ⭐⭐
题目大意
给定n条边ai, 每条边的长度是2ai; 问这n条边能组成多少个三角形, 不去重;
解题思路
本题因为是二进制所以难度大大降低; 当三条边都不一样时, 第三边一定小于两边之和, 也一定小于等于两边之差, 所以一定不行; 所以要考虑等腰和等边就可以了, 等腰只要第三边比腰短就行, 而等边更是用组合数就可以搞定; 我这里为了方便都对multiset和set来解决去重的问题;
神秘代码
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define endl '\n'
using namespace std;
const int N = 1e6 + 10, mod = 1e9 + 7;
typedef pair<int, int> PII;
int n, m;
int p[N];
signed main() {
int t;
cin >> t;
while(t--){
cin >> n;
multiset<int> s1;
set<int> s2;
for(int i = 1; i <= n; i++){
int a;
cin >> a;
s1.insert(a);
s2.insert(a);
}
int idx = 0, res = 0;
for(auto t : s2){
int num = s1.count(t);
if(num >= 2){
res += idx * num * (num - 1) / 2;
if(num >= 3){
res += num * (num - 1) * (num - 2) / 6;
}
}
idx += num;
}
cout << res << endl;
}
return 0;
}
C - Closest Cities
难度: ⭐⭐⭐
题目大意
有n个城市ai位于横轴上, 每个城市的距离是|ai - aj|; 而对于ai而言, 离其最近的城市称为友好城市, 题目保证每个城市都只能有一个友好城市; 从ai到其友好城市只需要花费一块钱, 而去别的城市需要花费|ai - aj|; 现在有m组询问, 给定起点和终点, 请问从起点到终点最少需要花费多少钱;
解题思路
看似是个图论, 但是因为都在横轴上, 所以不需要考虑路径问题, 就单向从起点到终点即可, 预处理出友好城市以及前缀和和后缀和既可以了; 要注意的是前缀和和后缀和是路径的和, 不是城市的和, 要注意求解时的下标;
神秘代码
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define endl '\n'
using namespace std;
const int N = 1e6 + 10, mod = 1e9 + 7;
typedef pair<int, int> PII;
int n, m;
int p[N], nx[N];
int u[N], du[N];
signed main() {
int t;
cin >> t;
while(t--){
cin >> n;
for(int i = 1; i <= n; i++) cin >> p[i];
for(int i = 1; i <= n; i++){
if(i - 1 >= 1 && i + 1 <= n){
if(abs(p[i] - p[i - 1]) < abs(p[i] - p[i + 1])){
nx[i] = i - 1;
}
else nx[i] = i + 1;
}
else if(i - 1 >= 1) nx[i] = i - 1;
else if(i + 1 <= n) nx[i] = i + 1;
}
for(int i = 2; i <= n; i++){
if(nx[i - 1] == i) u[i] = u[i - 1] + 1;
else u[i] = u[i - 1] + p[i] - p[i - 1];
}
for(int i = n - 1; i >= 1; i--){
if(nx[i + 1] == i) du[i] = du[i + 1] + 1;
else du[i] = du[i + 1] + p[i + 1] - p[i];
}
cin >> m;
while(m--){
int a, b;
cin >> a >> b;
if(a < b){
cout << u[b] - u[a] << endl;
}
else {
cout << du[b] - du[a] << endl;
}
}
}
return 0;
}
D - Berserk Monsters
难度: ⭐⭐⭐⭐
题目大意
现在有n个怪兽, 他们都有各自的攻击力和防御力, 当遇到大于防御力的攻击时会被击败, 防御力不会因受到攻击而被削减; 总共有n轮攻击, 每轮攻击怪兽都会向当前左边离自己最近的和右边离自己最近的怪兽发起攻击; 输出每一轮都有多少怪兽被击败;
解题思路
暴力模拟的复杂度是O(n2)需要优化; 模拟一下过程会发现, 当有怪兽被击败时, 只有相邻的两个怪兽受到影响; 如果某一轮没有怪兽被击败, 那么往后的轮次也不会有怪兽被击败; 我们可以用双向链表来表示怪兽之间相邻的关系; 用一个集合来装当前轮次被击败的怪兽, 然后通过检查其相邻的怪兽来得到下一轮被击败的怪兽; 由于每一轮被击败的怪兽都不一样, 所以这是O(n)的;
神秘代码
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define endl '\n'
using namespace std;
const int N = 3e5 + 10, mod = 1e9 + 7;
typedef pair<int, int> PII;
int n, m;
int a[N], d[N];
bool st[N];
struct {
int l, r;
}p[N];
signed main() {
IOS;
int t;
cin >> t;
while(t--){
cin >> n;
set<int> s1, s2;
for(int i = 1; i <= n; i++) st[i] =false;
st[0] = true, st[n + 1] = true;
a[0] = 0, a[n + 1] = 0;
for(int i = 1; i <= n; i++) cin >> a[i];
for(int i = 1; i <= n; i++) cin >> d[i];
for(int i = 1; i <= n; i++){
p[i].l = i - 1;
p[i].r = i + 1;
if(i == n) {
if(a[i - 1] > d[i]) s1.insert(i);
}
else if(a[i - 1] + a[i + 1] > d[i]){
s1.insert(i);
}
}
for(int i = 1; i <= n; i++){
cout << s1.size() << ' ';
for(auto t : s1){
st[t] = true;
int l = p[t].l, r = p[t].r;
p[l].r = r;
p[r].l = l;
}
for(auto t : s1){
int l = p[t].l, r = p[t].r;
if(!st[l] && a[p[l].l] + a[p[l].r] > d[l]) s2.insert(l);
if(!st[r] && a[p[r].l] + a[p[r].r] > d[r]) s2.insert(r);
}
s1.swap(s2);
s2.clear();
}
cout << endl;
}
return 0;
}
E - Increasing Subsequences
难度: ⭐⭐⭐
题目大意
给定一个数X, 构造一个长度最大为200的序列, 该序列的严格递增子序列的数量为x; 空序列也算是递增子序列; 注意[2, 2]可以产生三种递增子序列: 空序列, [2(第一个)], [2(第二个)];
解题思路
一个长度为n的递增序列可以产生2n个递增子序列; 所以我们可以找到比X小的最大的2n次方, 设X - 2n剩余的数量为x, 那么接下来再凑x; 之前的2n可以用1 ~ n的序列来产生, 但是以相同的方法凑会超出200的限制, 所以要考虑优化, 可以考虑借助前面序列里的数; 因为2n是最大的, 也就是说往后序列里的数都会小于n, 如果我们在1~n后面插入一个a, 那么就可以再产生新的2n-1个递增子序列, 因为可以借助1 ~ n 中 1 ~ a-1; 但是对于这个新的1 ~ a的序列, 由于其中1 ~ a-1已经用过了, 所以新生成的子序列都必须带有a, 所以也就是求1 ~ a-1的组合数, 也就是2n-1;
所以我们只需要找到x的二进制是1的位数i, 然后将i + 1插入到1 ~ n后面, 要注意为了避免相互影响, i也要从大的开始插;
神秘代码
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define endl '\n'
using namespace std;
const int N = 3e5 + 10, mod = 1e9 + 7;
typedef pair<int, int> PII;
int n, m;
int p[N];
signed main() {
IOS;
int t;
cin >> t;
while(t--){
int x;
cin >> x;
vector<int> res;
int num = 2;
int cnt = 1;
while(num <= x) {
res.push_back(cnt++);
num *= 2;
}
x -= num / 2;
for(int i = 60; i >= 0; i--) {
if((x >> i) & 1) res.push_back(i + 1);
}
cout << res.size() << endl;
for(int t : res) cout << t << ' ';
cout << endl;
}
return 0;
}