AtCoder Beginner Contest 248 A~F 题解 (UNIQUE VISION Programming Contest 2022)

UNIQUE VISION Programming Contest 2022(AtCoder Beginner Contest 248) 题解

A - Lacked Number

题意

给你一串包含0~9的数字串,找到没出现那个

做法

直接模拟

AC代码

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+7;
typedef long long ll;
int A[1000];
int main(){
	string s;
	cin>>s;
	for(char ch :s){
		A[ch] = 1;
	}
	for(char ch = '0';ch<='9';ch++){
		if(A[ch]==0) cout<<ch;
	}
	return 0;
}

B - Slimes

题意

问你从A增长到B,每次增长都乘K,至少需要乘多少次

思路

模拟

AC代码

在这里插入代码片

C - Dice Sum

题意

问你可以构造出多少种序列,使每个数都小于M,每个数之和小于K

思路

记忆化搜索,dp[i][j] 代表前i个数的总和是j的方案数,对于第i个数枚举从1
到M的数且当前总和小于K,往下搜就好了

AC代码

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+7;
typedef long long ll;
int A[1000];
int dp[100][5000];ll n,m,k;
ll mod = 998244353;
ll dfs(int step,int sum){
	if(dp[step][sum]!=-1) return dp[step][sum]%mod;
	if(step==n+1) return dp[step][sum] = 1;
	ll ans = 0;
	for(int i = 1;i<=m;i++){
		if(sum+i<=k) ans += dfs(step+1,sum+i)%mod;
	}
	return dp[step][sum] = ans%mod;
}
int main(){
	cin>>n>>m>>k;
	memset(dp,-1,sizeof(dp));
	cout<<dfs(1,0)%mod;
	return 0;
}

D - Range Count Query

题意

给你一个数组,q次询问L~R之间x出现了几次

思路

分别用数组记录所有数字出现的位置,这样每个数组都是有序的,在L~R查询x的次数时只需要在对应数组中二分查找L和R的位置,位置相减就是出现的次数

AC代码

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+7;
typedef long long ll;
vector<int> A[N];
int main(){
	int n;
	cin>>n;
	for(int i = 1;i<=n;i++){
		int t ;
		cin>>t;
		A[t].push_back(i); 
	}
	int q;
	cin>>q;
	while(q--){
		int bg,ex,x;
		cin>>bg>>ex>>x; 
		int pose = upper_bound(A[x].begin(),A[x].end(),ex)-A[x].begin()-1;
		int posb = lower_bound(A[x].begin(),A[x].end(),bg)-A[x].begin();
		int ans = pose-posb+1;
		cout<<max(0,ans)<<endl; 
	}
	return 0;
}

E - K-colinear Line

题意

给你n个点,问你有多少条不同的直线经过至少m的点

思路

我做的时候没考虑精度问题,直接算的斜率和截距丢进map去重导致错了
正确做法:
对于三个点来说判断其中一个点是否在另外两个点组成的直线上,可以用斜率判断 即 (y3-y1) / (x3-x1) == (y2-y1) / (x2-x1) , 考虑精度问题 ,转化为乘法
(y3-y1)(x2-x1) == (y2-y1)(x3-x1)
另外那对于直线怎么去重 : 考虑有k个点在一条直线上,不去重会产生(k-1)*k /2 条直线,那么我们在最后的时候把这个k个点上产生的答案乘上不去重产生的直线的倒数,那不就是正确的吗,即乘上(2/((k-1)*k))

AC代码

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+7;
typedef long long ll;
int x[N],y[N],ans[N];
int main(){
	int n,m;
	cin>>n>>m;
	for(int i = 0;i<n;i++){
		cin>>x[i]>>y[i];
	} 
	for(int i = 0;i<n;i++){
		for(int j = i+1;j<n;j++){ // 枚举边 
			int cnt = 0;
			for(int k = 0;k<n;k++){
				int y1 = (y[j]-y[i])*(x[k]-x[i]);
				int x1 = (y[k]-y[i])*(x[j]-x[i]);
				if(x1==y1) cnt++;
			}
			ans[cnt]++;
		}
	}
	if(m==1) cout<<"Infinity";
	else {
		ll res = 0;
		for(int i = m;i<=n;i++){
			res += ans[i]*2/(i*(i-1));
		}
		cout<<res;	
	}
	
	return 0;
}


F - Keep Connect

题意

给你n列这样的图在这里插入图片描述
问你对于删除 1~ (n-1) 条边还联通的不同图的数量,例如n等于3时,答案为7 15
这是删除1条边
删除1条边时
这是删除2条边
删除2条边时

思路

参考了下dls的视频,说说我的理解
这是个连通性DP,DP[i][j][k] 代表 前 i 列已经删去j条边,并且第i列中上下两个点的连通性为k (0代表不连通,反之)
接下来考虑每个状态的对后面状态的贡献值(刷表法)
我们设p1为第i列和i+1列的上边,p2为第i列和i+1列的下边,p3为第i列的对边

  1. k为1时,即第i列上下两点联通,那么这个状态对p1,p2,p3任取两条边时
    都有贡献
    2 k为0时,即第 i 列上下两点不连通,那么这个状态只对p1,p2都是联通的状态有贡献,因为其中上下一条边删去时,无论后面的联通性如何,第i列会存在无法联通的点

AC代码

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+7;
typedef long long ll;
int dp[3100][3100][2]; // 前i个点去除j条边第i的连通性为k(0,1)
int main(){
	int n,p;
	cin>>n>>p;
	dp[0][0][1] = 1;
	dp[0][1][0] = 1;
	for(int i = 0;i<n;i++)
	for(int j = 0;j<n;j++)
	for(int k = 0;k<2;k++)       // 第i列的边 
	if(dp[i][j][k]){
		for(int p1 = 0;p1<2;p1++) // 上邻边 
		for(int p2 = 0;p2<2;p2++)// 下邻边 
		for(int p3 = 0;p3<2;p3++)// 对边 
		{
			if(k==0){
				if(p1==0||p2==0) continue;
				dp[i+1][j+(1-p3)][p3] = (dp[i+1][j+(1-p3)][p3]+dp[i][j][k])%p;
			}	
			else{
				if(p1==0&&p2==0) continue;
				dp[i+1][j+3-p1-p2-p3][(p1+p2+p3)>=2] = (dp[i+1][j+3-p1-p2-p3][(p1+p2+p3)>=2]+dp[i][j][k])%p;
			}
		}
	}
	for(int i = 1;i<n;i++){
		cout<<dp[n-1][i][1]<<" ";
	}
	return 0;
}

经验总结

E题的去重想法有点像容斥定理,都是先计算,后面再减去由此产生多余的答案,以后的话可以在处理复杂问题可以计算,再减去重复贡献,还有涉及几何要考虑精度,用分数存

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
AtCoder Beginner Contest 134 是一场 AtCoder 的入门级比赛,以下是每道题的简要题解: A - Dodecagon 题目描述:已知一个正十二边形的边长,求它的面积。 解题思路:正十二边形的内角为 $150^\circ$,因此可以将正十二边形拆分为 12 个等腰三角形,通过三角形面积公式计算面积即可。 B - Golden Apple 题目描述:有 $N$ 个苹果和 $D$ 个盘子,每个盘子最多可以装下 $2D+1$ 个苹果,求最少需要多少个盘子才能装下所有的苹果。 解题思路:每个盘子最多可以装下 $2D+1$ 个苹果,因此可以将苹果平均分配到每个盘子中,可以得到最少需要 $\lceil \frac{N}{2D+1} \rceil$ 个盘子。 C - Exception Handling 题目描述:给定一个长度为 $N$ 的整数序列 $a$,求除了第 $i$ 个数以外的最大值。 解题思路:可以使用两个变量 $m_1$ 和 $m_2$ 分别记录最大值和次大值。遍历整个序列,当当前数不是第 $i$ 个数时,更新最大值和次大值。因此,最后的结果应该是 $m_1$ 或 $m_2$ 中较小的一个。 D - Preparing Boxes 题目描述:有 $N$ 个盒子和 $M$ 个物品,第 $i$ 个盒子可以放入 $a_i$ 个物品,每个物品只能放在一个盒子中。现在需要将所有的物品放入盒子中,每次操作可以将一个盒子内的物品全部取出并分配到其他盒子中,求最少需要多少次操作才能完成任务。 解题思路:首先可以计算出所有盒子中物品的总数 $S$,然后判断是否存在一个盒子的物品数量大于 $\lceil \frac{S}{2} \rceil$,如果存在,则无法完成任务。否则,可以用贪心的思想,每次从物品数量最多的盒子中取出一个物品,放入物品数量最少的盒子中。因为每次操作都会使得物品数量最多的盒子的物品数量减少,而物品数量最少的盒子的物品数量不变或增加,因此这种贪心策略可以保证最少需要的操作次数最小。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值