ATcoder Beginner Contest 371个人题解

AtCoder Beginner Contest 371

网址:ATcoder Beginner Contest 371

A.Jiro

题目翻译:

给定三个符号,分别是 S A B , S A C , S B C S_{AB}, S_{AC}, S_{BC} SAB,SAC,SBC,这三个符号只有 > 和 < > 和 < ><两种。表示第一个字母和第二个字母的关系。问:既不是最大也不是最小的那个字母是谁?

题解:

签到题。

这里给出几种常见的解法。

题解A:

直接暴力打表,主要是别写错就可以了。

#include<iostream>
using namespace std;
int main(){
	char a, b, c;
	cin>>a>>b>>c;
	if(a=='<'){
		if(c=='<')cout<<'B'<<endl;
		else{
			if(b=='<')cout<<'C'<<endl;
			else cout<<'A'<<endl;
		}
	}
	else{
		if(c=='>')cout<<'B'<<endl;
		else{
			if(b=='<')cout<<'A'<<endl;
			else cout<<'C'<<endl;
		}
	}
	return 0;
} 

题解B

把所有的大鱼号和小鱼号看成1和0

那么每一种其实是一个状态,你只需要把每个状态枚举出来就可以了。三个符号8种状态,离散学过的。

#include<iostream>
using namespace std;

int main(){
	char a[4];
	for(int i=1; i<=3; ++i)cin>>a[i];
	int num[3];
	for(int i=1; i<=3; ++i){
		num[i-1]=(a[i]=='<');
	}	
	int ans=num[0]*4+num[1]*2+num[2];
//	cout<<ans<<endl;
	switch(ans){
		case 1:case 6:{
			cout<<'C';
			break;
		}
		case 0:case 7:{
			cout<<'B';
			break;
		}
		case 3:case 4:{
			cout<<'A';
			break;
		}
		default:{
			cout<<"E"<<endl;
			break;
		}
	}
	return 0;
}

题解C

这是一种没有什么用的解法。

我们思考这样的问题。反正就八个样例,你直接归个类。

我们注意到如果第二个字和第三个字不一样,那么不是无解就是C。因为不会有无解的样例所以可以直接输出C。然后就剩下四种情况了,且二三必定相同,那么观察得如果一二不同就是A,相同就是B

非常神奇,所以code也比较友好。

#include<iostream>
using namespace std;
int main(){
	char a, b, c;
	cin>>a>>b>>c;
	bool A, B, C;
	A=(a=='<');
	B=(b=='<');
	C=(c=='<');
	if(B^C)cout<<'C'<<endl;
	else if(A^B)cout<<'A'<<endl;
		 else cout<<'B'<<endl; 
	return 0;
} 

个人喜欢解法A因为我视力不好,观察时间太长

B.谁是长子

题目翻译

给你一个n一个m。表示一共n个从来没有孩子的家庭按顺序(?)生了m个小孩,并按顺序告诉你他们属于哪个家庭,性别是啥。问这个孩子是不是长子。

题解:

签到题plus

那很明显啊长子这个东西显然是一个萝卜一个坑的东西对吧,所以你就把坑提前开好就可以了,如果这个家庭生了个男孩就跳进去然后显示里面有人,并输出一个YES,如果还没跳坑里就有人了那就直接原地No了,女孩包No的。

#include<iostream>
#include<cstring>
using namespace std;
const int N=110;
bool wc[N];
int main(){
	int n, m;
	cin>>n>>m;
	memset(wc, 0, sizeof wc);
	for(int i=1; i<=m; ++i){
		int u;
		char v;
		cin>>u>>v;
		if(v=='F')cout<<"No"<<endl;
		else{
			if(wc[u])cout<<"No"<<endl;
			else {
				cout<<"Yes"<<endl;
				wc[u]=true;
			}
		} 
	}
	return 0;
}

advanced:

如果这道题的u是到2e9,在u的种类是2e5的情况下也是可以做的开个map即可

C.同构

读了半天题没看懂讲的是啥

题目翻译:

给两张图,一张G一张H,以及这张H链接或者删除i和j的代价,需要你用最小的代价使H和G同构。

解释同构:同构是指GH的形状相同 ⇒ \Rightarrow G本身有编号,你给H编号,存在一种编号是G

题解

这是一道教学你怎么next_permulation的题目。

这里给出next_permulation最基础的解法:如果你想知道一个数组比它字典序稍微小一点,且只小一点的排列,(通俗地),你可以直接

next_permulation(a+1,a+n+1);

知道这个之后就很明显了。算法步骤如下:

  • 获取一个新的排列
  • 把这个节点序列按顺序排成升序
  • 将新的图与G进行比对
    • 如果节点的所有边都与G相同,那么这个节点合格
    • 如果不同,多退少补,res+ w i j w_{ij} wij
  • 循环到第一步

做到什么时候为止呢?

一个数列的排列,有且仅 n ! n! n!种,所以你就循环 n ! − 1 n!-1 n!1就好了,因为初始12345不用循环,当然,边界问题并不重要,你甚至可以循环 n k n^k nk,反正也不好超时,不用特殊耗费脑子

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=100;
int h[3][N], ne[3][N], e[3][N], idx[3];
int w[10][10];

void add(int a, int b, int id){
	int i=id;
	int idd=idx[i];
	e[i][idd]=b;
	ne[i][idd]=h[i][a];
	h[i][a]=idx[i]++;
}
int p[N]={0,1,2,3,4,5,6,7,8,9,10};
int behave(int i){
	if(i==0 || i==1)return i;
	return i*behave(i-1);
}
int solve(int n){
	int ans=0;
	for(int i=1; i<=n; ++i){
		bool st[10];
		memset(st, 0, sizeof st);
		for(int j=h[1][i]; j!=-1; j=ne[1][j]){
			st[e[1][j]]=true;
		}	
		for(int j=h[2][p[i]]; j!=-1; j=ne[2][j]){
			int x=e[2][j];
			for(int k=1; k<=n; ++k){
				if(p[k]==x){
					x=k;
					break;
				}
			}
			if(x<=i)continue;
			if(st[x]==false){
				ans+=w[p[i]][p[x]];
			}
			else st[x]=false;
		}
		for(int j=i+1; j<=n; ++j){
			if(st[j]==true){
				ans+=w[p[i]][p[j]];
			}
		}
	}
	return ans;
}
int main(){
	int n;
	cin>>n;
	memset(h, -1, sizeof h);
	memset(idx, 0, sizeof idx);
	for(int i=1; i<=2; ++i){
		int m;
		cin>>m;
		for(int j=1; j<=m; ++j){
			int u, v;
			cin>>u>>v;
			add(v, u, i);
			add(u, v, i);
		}
	}	
	for(int i=1; i<n; ++i){
		w[i][i]=0;
		for(int j=i+1; j<=n; ++j){
			int k;
			cin>>k;
			w[i][j]=k;
			w[j][i]=k;
		}
	}
	int ans=0x3f3f3f3f;
	for(int i=1; i<=behave(n); ++i){
		next_permutation(p+1, p+1+n);
		ans=min(ans, solve(n));
	}
	cout<<ans<<endl;
	return 0;
}

D.在只有x轴的MC里数村民

题目翻译

给定 n n n个村庄和一个横轴, n n n个村庄在不同的横坐标,每个村庄有 p p p个村民,然后是 Q Q Q次问询,每次问询给定 L R LR LR,求区间内村民的总数

题解:

显然区间求和最简单易懂的前缀和问题。

直接前缀和打上去,然后找 L 和 R L和R LR分别在那里,分析一下复杂度,爆搜包寄的,所以应该就是一个二分。

前缀和显然是 O ( 1 ) O(1) O(1)的查找, O ( N ) O(N) O(N)的预处理,然后二分是一个 O ( l o g n ) O(logn) O(logn)的复杂度,整体的复杂度应该是 m a x ( Q l o g n , N ) max(Qlogn, N) max(Qlogn,N)这样的一个复杂度,还是可以过的

#include<iostream>
#include<algorithm>
using namespace std;
const int N=2e5+100;

struct node{
	int x, p;
}e[N];
bool cmp(node a, node b){
	return a.x<b.x;
}
int n;
int find(int x){
	int l=1, r=n+1;
	while(l<r){
		int mid=l+r>>1;
		if(e[mid].x>=x)r=mid;
		else l=mid+1;
	}
	if(x<=e[l].x)return l;
	else return r;
}
long long w[N];
int main(){
	cin>>n;
	for(int i=1; i<=n; ++i)cin>>e[i].x;
	for(int i=1; i<=n; ++i)cin>>e[i].p;
	e[n+1].x=0x3f3f3f3f;
	e[n+1].p=0;
	w[0]=0;
	for(int i=1; i<=n; ++i)w[i]=w[i-1]+e[i].p;
	
	sort(e+1, e+n+2, cmp);

	int q;
	cin>>q;
	for(int i=1; i<=q; ++i){
		int l, r;
		cin>>l>>r;
		int xl=find(l);
		int xr=find(r);
		if(e[xr].x!=r)xr--;
		cout<<w[xr]-w[xl-1]<<endl;
	}
	
	return 0;
}

E.唐题

题目翻译:

给定一串数列,问你它的 ∑ i = 1 n ∑ j = i n d i s t i , j \sum_{i=1}^n\sum_{j=i}^ndist_{i,j} i=1nj=indisti,j d i s t i , j dist_{i,j} disti,j [ i , j ] [i,j] [i,j]区间里不同数字的个数

题解:

这是一个序列,而且要求的东西是一些有规律的区间,所以可以考虑元素的贡献队答案的贡献。对于每个元素,我们需要考虑针对于以这个元素为左端点的所有区间,它后面的元素对这些区间的总贡献。

那么我们可以得到这样的结论:

在第 i 位置 ( i ∈ [ 1 , n ] ) 的元素 = { 0 [ l , i ) 中存在 a i n − i + 1 e l s e 在第i位置(i\in [1, n])的元素=\begin{cases}0&[l, i)中存在a_i\\n-i+1&else \end{cases} 在第i位置(i[1,n])的元素={0ni+1[l,i)中存在aielse

那么明显的,我们可以得到一张大表。

然后把这张表上所有的数据加在一起,显然就是结论了。那么我们考虑如何去优化它。

针对于在位置 i i i上的元素 a i a_i ai,它最多被加 n − i + 1 n-i+1 ni+1共加 i i i次。那么如果前面有一个 a j = a i ( j < i ) a_j=a_i(j<i) aj=ai(j<i),那么它就会被置 ( j − 1 ) (j-1) (j1) 0 0 0,那么我们就知道,我们只需要检索它左面最近的和它相同的数和它的距离 ( i − j ) (i-j) (ij),那么 a i a_i ai a n s ans ans的总贡献应该是 ( i − j ) × ( n − i + 1 ) (i-j)\times (n-i+1) (ij)×(ni+1)

所以答案应该是 ∑ i = 1 n ( i − j ) × ( n − i + 1 ) \sum_{i=1}^{n}(i-j)\times (n-i+1) i=1n(ij)×(ni+1)并且 a j = a i a_j=a_i aj=ai ∄ k ∈ ( j , i ) , a k = a i \not\exist k\in(j, i), a_k=a_i k(j,i),ak=ai

#include<iostream>
#include<cstring>
using namespace std;
const int N=2e5+100;
int a[N];
long long net[N], lst[N];
int main(){
	int n;
	cin>>n;
	memset(lst, 0, sizeof lst);
	long long ans=0;
	for(int i=1; i<=n; ++i){
		cin>>a[i];
		ans+=(n-i+1)*(i-lst[a[i]]);
		lst[a[i]]=i;
	}
	cout<<ans<<endl;
	return 0;
}

F.

G.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值