写一下本场ABC371前五题的题解,先贴个比赛链接https://atcoder.jp/contests/abc371 ————
目录
A题(Jiro)
思路分析
简单题,分情况讨论一下即可
代码实现
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <climits>
#include <iomanip>
#include <cmath>
#include <string>
#include <vector>
#define int long long
#define x first
#define y second
using namespace std;
const int N = 1e5 + 10;
int a[N];
void solve()
{
//ab,ac,bc
string a,b,c;
cin >> a >> b >> c;
if(a == ">" && b == ">" && c == ">") cout << "B" << endl;
else if(a == "<" && b == "<" && c == "<") cout << "B" << endl;
else if(a == ">" && b == "<" && c == "<") cout << "A" << endl;
else if(a == "<" && b == ">" && c == ">") cout << "A" << endl;
else cout << "C" << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr),cout.tie(nullptr);
int t = 1;
/* int t;
cin >> t; */
while(t -- ) solve();
return 0;
}
B题(Taro)
思路分析
可以用一个set容器维护一下是否出现过Taro,如果没出现过insert进去,也可以开一个hash数组
代码实现
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <climits>
#include <iomanip>
#include <cmath>
#include <string>
#include <vector>
#define int long long
#define x first
#define y second
using namespace std;
const int N = 1e5 + 10;
int a[N];
void solve()
{
int n,m;
cin >> n >> m;
/* vector<bool> hash(n);
while(m -- )
{
int p;
char op;
cin >> p >> op;
if(!hash[p] && op == 'M')
{
hash[p] = true;
cout << "Yes" << endl;
}
else cout << "No" << endl;
} */
set<int> st;
while(m -- )
{
int p;
char op;
cin >> p >> op;
if(op == 'M')
{
if(st.count(p)) cout << "No" << endl;
else
{
cout << "Yes" << endl;
st.insert(p);
}
}
else cout << "No" << endl;
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr),cout.tie(nullptr);
int t = 1;
/* int t;
cin >> t; */
while(t -- ) solve();
return 0;
}
C题(Make Isomorphic)
思路分析
C题这个题面描述实在太抽象,看了半天才明白啥意思,但其实放在第三题不会特别难,我们先来理清一下题意,现在有两个无向图,我们只对第二个操作(删边或者加边),使其变成第一个无向图那种形状,即无向图的同构,需要消耗的最小代价,每条边和每条边之间的代价已经输入了,我们可以发现N是小于8的,那我们直接模拟,求其全排列,取其中一种代价的最小值即可
下面说下同构的定义,其实我觉得还是很抽象的:
除去顶点索引可以不一样,其他两张图的形状相同,换句话说就是把{1,2, 3 ---- n}进行重排,变成{p1,p2,p3----pn},当且仅当(i,j)有边时,(pi,pj)有边
代码实现
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <climits>
#include <iomanip>
#include <cmath>
#include <string>
#include <vector>
#define int long long
#define x first
#define y second
using namespace std;
int n, m1, m2;
//用邻接矩阵存图
int a[10][10] = {0};
int b[10][10] = {0};
int w[10][10] = {0};
int p[10];
signed main(){
scanf("%d %d", &n, &m1);
for(int i = 1; i <= m1; ++ i){
int x, y;
cin >> x >> y;
a[x][y] = a[y][x] = 1;
}
scanf("%d", &m2);
for(int i = 1; i <= m2; ++ i){
int x, y;
cin >> x >> y;
b[x][y] = b[y][x] = 1;
}
for(int i = 1; i <= n; ++ i){
for(int j = i + 1; j <= n; ++ j){
scanf("%d", &w[i][j]);
w[j][i] = w[i][j];
}
}
//初始化
for(int i = 1; i <= n; ++ i){
p[i] = i;
}
long long ans = 1e17;
do{
//计算费用
long long sum = 0;
for(int i = 1; i <= n; ++ i){
for(int j = i + 1; j <= n; ++ j){
if(a[i][j] != b[p[i]][p[j]]){
sum += w[p[i]][p[j]];
}
}
}
ans = min(ans, sum);
}while(next_permutation(p + 1, p + n + 1));
printf("%lld", ans);
//return 0;
/* 如果 a[i][j](表示第一个图中节点 i 和节点 j 是否有边)与 b[p[i]][p[j]](表示第二个
图中节点 p[i] 和节点 p[j] 是否有边)不相等,
说明两个图的边在这个排列下不匹配,需要支付权重 w[p[i]][p[j]]。 */
}
D题(1D Country)
思路分析
D题的思路还是很好想明白的,先考虑前缀和,然后用每次给的L和R二分查找把两个计算区间边界算出来,最终相减即可,但是要注意下标越界的情况,若下界小于0则把他的值重新变成0。
代码实现
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <climits>
#include <iomanip>
#include <cmath>
#include <string>
#include <vector>
#define int long long
#define x first
#define y second
using namespace std;
const int N = 2e5 + 10;
int a[N];
int X[N],P[N],pre[N];
void solve()
{
int n;
cin >> n;
for(int i = 1; i <= n; i++) cin >> X[i];
for(int i = 1; i <= n; i++) cin >> P[i];
for(int i = 1; i <= n; i++) pre[i] = pre[i - 1] + P[i];
//for(int i = 0; i <= n; i++) cout << pre[i] << " ";
//cout << endl;
int Q;
cin >> Q;
while(Q --)
{
int l,r;
cin >> l >> r;
int L = lower_bound(X + 1, X + 1 + n, l) - X;
int R = upper_bound(X + 1, X + 1 + n, r) - X - 1;
// cout << L << " " << R << endl;
if(L > R) cout << "0" << endl;
else
{
if(L < 0) L = 0;
cout << pre[R] - pre[L - 1] << endl;
}
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr),cout.tie(nullptr);
int t = 1;
/* int t;
cin >> t; */
while(t -- ) solve();
return 0;
}
二分查找技巧
这里我补充一个lower_bound和upper_bound的二分查找用法(方便后续复习食用哈哈)——
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <climits>
#include <iomanip>
#include <cmath>
#include <string>
#include <vector>
using namespace std;
const int maxn=100000+10;
const int INF=2*int(1e9)+10;
#define LL long long
int cmd(int a,int b){
return a>b;
}
int main(){
int num[6]={1,2,4,7,15,34};
sort(num,num+6); //按从小到大排序
int pos1=lower_bound(num,num+6,1)-num; //返回数组中第一个大于或等于被查数的值
int pos2=upper_bound(num,num+6,7)-num; //返回数组中第一个大于被查数的值
cout<<pos1<<" "<<num[pos1]<<endl;
cout<<pos2<<" "<<num[pos2]<<endl;
sort(num,num+6,cmd); //按从大到小排序
int pos3=lower_bound(num,num+6,7,greater<int>())-num; //返回数组中第一个小于或等于被查数的值
int pos4=upper_bound(num,num+6,7,greater<int>())-num; //返回数组中第一个小于被查数的值
cout<<pos3<<" "<<num[pos3]<<endl;
cout<<pos4<<" "<<num[pos4]<<endl;
return 0;
}
E题(I Hate Sigma Problems)
思路分析
我觉得这题就是一个思维+推公式的题,考虑每次加进来一个数对最终答案的贡献,假设f(i) 为加进来a[i]后带来的贡献值,如果a[i + 1]还未出现,则 f(i + 1) 为 f(i) + i + 1 , 再仔细思考一下如果a[i + 1]已经出现过了,则f(i + 1) = f(i) + i + 1 - pos(a[i]) ,其中pos(a[i]) 为前面最后一次出现a[i]的位置,这个确实很难想,笔者也是比赛结束看别人思路才发现的这个规律,真的很奇妙
代码实现
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <climits>
#include <iomanip>
#include <cmath>
#include <string>
#include <vector>
#define int long long
#define x first
#define y second
using namespace std;
const int N = 2e5 + 10;
//int a[N];
void solve()
{
int n;
cin >> n;
vector<int> pos(n + 1);
int res = 0;
int sum = 0;
for(int i = 1; i <= n; i++)
{
int x;
cin >> x;
res = res + sum + (i - pos[x]);
sum = sum + (i - pos[x]) ;
// cout << res << " " << sum << endl;
pos[x] = i;
}
cout << res << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr),cout.tie(nullptr);
int t = 1;
/* int t;
cin >> t; */
while(t -- ) solve();
return 0;
}
//f(i + 1) = f(i) + i + 1; ————a[i]在之前未出现过,加上它对答案的贡献
//f(i + 1) = f(i) + i + 1 - pos(a[i])————a[i]在之前出现过,加上它对答案的贡献,pos[i]为a[i]最晚出现的位置
ABC如果每次能稳做4题及4题以上倒也挺好,还是需要不断的训练,明天补一下牛客周赛,拜拜~