AtCoder Beginner Contest 371 A-E 题解

A 和 C 都很扯(bushi,看看我的做题顺序就知道

pAulJlq.jpg
UPD: 看我说什么来着
pAuYYc9.jpg

A. Jiro

题意

给你三个符号 S A B , S A C , S B C S_{AB},S_{AC},S_{BC} SAB,SAC,SBC

定义:若 S i , j S_{i,j} Si,j> i > j i>j i>j,否则 i < j i<j i<j

A , B , C A,B,C A,B,C 中第 2 2 2 大的值

思路

这道题不知道有没有更好的方法,但分类讨论思路比较清晰
{ a > b :      b > c : b      b < c :          a > c : c          a < c : a a < b :      b < c : b      b > c :          a < c : c          a > c : a \begin{cases} a>b: \\ \ \ \ \ b>c:b \\ \ \ \ \ b<c: \\ \ \ \ \ \ \ \ \ a>c:c \\ \ \ \ \ \ \ \ \ a<c:a \\ a<b: \\ \ \ \ \ b<c: b \\ \ \ \ \ b>c: \\ \ \ \ \ \ \ \ \ a<c:c \\ \ \ \ \ \ \ \ \ a>c:a \end{cases} a>b:    b>c:b    b<c:        a>c:c        a<c:aa<b:    b<c:b    b>c:        a<c:c        a>c:a

C++ 代码
#include<bits/stdc++.h>
using namespace std;
char ab,ac,bc;
int main(){
   	cin>>ab>>ac>>bc;
	if(ab=='>'){
		if(bc=='>'){
			cout<<'B';
		}else{
			if(ac=='>'){
				cout<<'C';
			}else{
				cout<<'A';
			}
		}
	}else{
		if(bc=='<'){
			cout<<'B';
		}else{
			if(ac=='<'){
				cout<<'C';
			}else{
				cout<<'A';
			}
		}
	}
	return 0;
}

B. Taro

题意

n n n 个 家庭,按顺序 出生 m m m 个婴儿,每次婴儿出生于第 A i A_i Ai 个家庭,性别为 B i B_i Bi(男性为 M,女性为 F

判断第 i   ( 1 ≤ i ≤ m ) i\ (1 \le i \le m) i (1im) 个婴儿是 ( Yes ) 否 ( No ) 是 第 A i A_i Ai 个家庭的最早出生的 男性 婴儿(称为 Taro)。

思路

用一个 used[n] 数组记录第 i i i 个家庭是否已经有了 Taro

每次输入时,若是女性,直接输出 No 后跳过

否则判断 used[a_i] 是否为 1 1 1
若是,输出 No;否则输出 Yes 并把 used[a_i] 标为 1 1 1

C++ 代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=105;
bool used[maxn];
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=0;i<m;i++){
		int ai;
		char c;
		cin>>ai>>c;
		if(c=='F'){
			cout<<"No"<<endl;
		}else{
            if(!used[ai]){
                cout<<"Yes"<<endl;
                used[ai]=true;
            }else{
                cout<<"No"<<endl;
            }
        }
	}
	return 0;
}

C. Make Isomorphic

题意

题意很复杂

给你两个无向图 G G G H H H,有 N N N 个顶点,分别 M G M_G MG M H M_H MH 条边。

给你在每两个点删掉一条边或加上一条边的代价,你需要在 H H H 上添加或删减边,使得 H H H G G G 同构

求最小总代价

同构定义:

除去顶点索引可以不一样,其它两张图形状相同,换句话说

{ 1 , 2 ,   . . . ,   n } \{1,2, \ ..., \ n\} {1,2, ..., n} 进行重排,变成 { p 1 , p 2 ,   . . . ,   p n } \{p_1,p_2,\ ..., \ p_n \} {p1,p2, ..., pn},当且仅当 ( i , j ) (i,j) (i,j) 有边时, ( p i , p j ) (p_i,p_j) (pi,pj) 有边

思路

需要用到 next_permutation() 函数,引用 bits/stdc++.h 头文件后即可使用

此函数共 2 2 2 个参数,next_permutation(a+1,a+n+1) 代表下标从 1 1 1 开始的 a a a 数组的 下一个排列(按照字典序)

由于 N ≤ 8 N \le 8 N8,所以最多有 8 ! = 40320 8!=40320 8!=40320 种排列方式,直接枚举全排列即可

图结构考虑用邻接矩阵存储,而不用邻接表,因为要快速查找 ( i , j ) (i,j) (i,j) 之间是否有边

C++ 代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=2e9;
const int maxn=10;
int tab[maxn][maxn];
int n;
int ng,nh;
bool g[maxn][maxn],h[maxn][maxn];
int pt[maxn];
signed main(){
    //输入
	cin>>n>>ng;
	for(int i=0;i<ng;i++){
		int ai,bi;
		cin>>ai>>bi;
		g[ai][bi]=true;
		g[bi][ai]=true;
	}
	cin>>nh;
	for(int i=0;i<nh;i++){
		int ai,bi;
		cin>>ai>>bi;
		h[ai][bi]=true;
		h[bi][ai]=true;
	}
	for(int i=1;i<=n;i++){
		for(int j=i+1;j<=n;j++){
			cin>>tab[i][j];
			tab[j][i]=tab[i][j];
		}
	}
    
    //初始化排列
    for(int i=1;i<=n;i++){
		pt[i]=i;
	}
    
    //开始枚举全排列
	int ans=inf;
	do{
		int res=0;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				if(i==j) continue;
				if(g[i][j]!=h[pt[i]][pt[j]]){
					res+=tab[pt[i]][pt[j]];
				}
			}
		}
        //因为是无向图每条边的代价都会被算2次,所以要在最后除以2
		ans=min(ans,res/2);
	}while(next_permutation(pt+1,pt+1+n));
    
	cout<<ans<<endl;
	return 0;
}

D. 1D Country

题意

N N N 个村庄,每个村庄在 X i X_i Xi 的位置,有 P i P_i Pi 个村民

回答 Q Q Q 次询问:找到位置 L L L R R R 之间的村民总个数

思路

考虑前缀和,其中 s u m 0 = 0 sum_0=0 sum0=0

每次给的 L L L R R R 用二分查找定位,最终相减。

注意下标越界的情况,若下界小于 0 0 0 则把他的值重新变为 0 0 0

C++ 代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int32_t t=1;
const int maxn=200005;
int n,q;
int x[maxn],sum[maxn];
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>x[i];
	}
	for(int i=1;i<=n;i++){
		int ai;
		cin>>ai;
		sum[i]=sum[i-1]+ai;
	}
	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;
		if(L>R){
			cout<<0<<endl;
		}else{
			L--;
			if(L<0) L=0;
			cout<<sum[R]-sum[L]<<endl;
		}
	}
	return 0;
}

E. I Hate Sigma Problems

题意

给你 a a a 数组,定义 f ( i , j ) f(i,j) f(i,j) a a a 数组中,下标从 i i i j j j 共有多少个不同种类的数字

∑ i = 1 N ∑ j = 1 N f ( i , j ) \displaystyle \sum_{i=1}^N \sum_{j=1}^N f(i,j) i=1Nj=1Nf(i,j)

思路

对于每个 i i i, 考虑下标起点为 i i i 的子数组

正常情况下若不重复, f ( i , j ) f(i,j) f(i,j) 的值 ( j ≥ i ) (j\ge i) (ji) 依次为 1 , 2 , 3 ,   . . . ,   n − i + 1 1,2,3,\ ..., \ n-i+1 1,2,3, ..., ni+1

但是,出现了重复,那么在重复的地方,后面每个都减一

那么我们预处理出总共需要减去的数即可,其余部分用等差数列求和公式进行求和

C++ 代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=200005;
int a[maxn];
queue<int> v[maxn];
int n;
int sum;
int ans;
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		v[a[i]].push(n-i+1);
		if(v[a[i]].size()>1){
			sum+=(n-i+1);
		}
	}
	for(int i=1;i<=n;i++){
		int res=(1+(n-i+1))*(n-i+1)/2;
		v[a[i]].pop();
		ans+=res-sum;
		if(v[a[i]].size()>=1){
			sum-=v[a[i]].front();
		}
	}
	cout<<ans<<endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值