AtCoder Beginner Contest 246 C~F题解

文章介绍了AtCoderBeginnerContest246中的几道编程题目,涉及利用贪心算法解决优惠券使用问题,寻找满足特定数学条件的最小数值,以及使用BFS解决网格路径规划问题和字符串组合计算。文章深入探讨了每道题目的解题思路和优化技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

AtCoder Beginner Contest 246

C

题意:要买n个商品,第 i i i个商品价格为 a i a_i ai,同时有k张优惠券,每张优惠券可以减免x元。

问要买下这n个商品最少要多少钱。

贪心,优先把大于x的商品用优惠券,若还有剩余,则优先把价格大的用优惠券减免。

code:

#include<bits/stdc++.h>

using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define x first
#define y second
typedef pair<int,int> pii;

//head
const int N=2e5+10,mod=998244353;
int a[N];
int n,k,x;
void work()
{
	cin>>n>>k>>x;
	int sum=0;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		sum+=a[i]/x;
		a[i]%=x;
	}
	int ans=0;
	if(sum>=k){
		ans+=(sum-k)*x;
		for(int i=1;i<=n;i++) ans+=a[i];
	}
	else {
		sort(a+1,a+1+n);
		k-=sum;
		for(int i=1;i<=n;i++) ans+=a[i];
		for(int i=n;i>=1;i--){
			if(!k) break;
			ans-=a[i];
			k--;
		}
	}
	cout<<ans<<endl;
	
}
signed main()
{
	
	int t;
	t=1;
	while(t--) work();

	return 0;
}

D

题意:给定N,求出最小的X,满足下列条件:

  1. X ≥ N X \geq N XN
  2. 存在一对非负整数 a , b , 满足: X = a 3 + a 2 b + a b 2 + b 3 存在一对非负整数a,b,满足:X=a^3+a^2b+ab^2+b^3 存在一对非负整数a,b,满足:X=a3+a2b+ab2+b3

0 ≤ N ≤ 10 18 0 \leq N \leq {10}^{18} 0N1018.

注意到N的数据范围为 10 18 {10}^{18} 1018,因此a或b的范围为 1 0 6 10^6 106 , 因此我们可以枚举a,然后二分找到满足条件的最小的b, 然后不断更新答案即可。

code:

#include<bits/stdc++.h>

using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define x first
#define y second
typedef pair<int,int> pii;

//head
const int N=2e5+10,mod=998244353;

int f(int a,int b)
{
	return a*a*a+a*a*b+a*b*b+b*b*b;
}

void work()
{
	int n;
	cin>>n;
	int ans=2e18;
	for(int i=0;i<=1e6;i++){
		int l=0,r=1e6;
		while(l<r)
		{
			int mid=l+r>>1;
			if(f(i,mid)>=n) r=mid;
			else l=mid+1;
		}
		ans=min(ans,f(i,r));
	}
	cout<<ans<<endl;

}
signed main()
{
	
	int t;
	t=1;
	while(t--) work();

	return 0;
}

E

题意:给定 N ∗ N N*N NN的网格,给出起点和终点坐标,同时给出障碍物的坐标,问从起点到终点最少要走几步。

走的规则为:只能沿对角线斜着走,若斜着的路径上没有障碍物,则可以一步到达。

官方题解是01BFS,判断当前的方向,若当前的方向跟要上一步的方向一致,则边权为0,若不一致,则边权为1。用双端队列来实现即可。

还有一种做法是,把某个方向的点,能加入队列都加入队列,然后跟普通的bfs一样,正常的做,唯一不同的是对于每个点来说,如果当前方向已经被扩展过,就不需要再把它入队,因此st数组要多开一维来记录方向。如果不这样的话,会有很多点被扩展进队列里导致超时。(个人感觉官方的题解更对一些,这个做法不是很好想到)

第二种方法code:

#include<bits/stdc++.h>

using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
//#define int long long
#define x first
#define y second
typedef pair<int,int> pii;

//head
const int N=2e3+10,mod=998244353;
char s[N][N];
int n;

bool st[N][N][4];
int sx,sy,ex,ey;
int dx[]={-1,1,1,-1},dy[]={1,1,-1,-1};

struct node{
	int x,y,w;
};
bool check(int a,int b,int i)
{
	if(a>n||b>n||a<1||b<1||s[a][b]=='#'||st[a][b][i]) return 0;
	return 1;
}
int bfs()
{
	queue<node> q;
	q.push({sx,sy,0});
	
	while(q.size())
	{
		auto t=q.front(); q.pop();
		int x=t.x,y=t.y,w=t.w;
		for(int i=0;i<4;i++){
			int a=x+dx[i],b=y+dy[i];
			
			while(check(a,b,i)) {
				q.push({a,b,w+1});
				st[a][b][i]=1;
				if(a==ex&&b==ey) return w+1;
				a+=dx[i]; b+=dy[i];
			}
		}
	}
	return -1;
}
void work()
{
	cin>>n;
	cin>>sx>>sy>>ex>>ey;
	for(int i=1;i<=n;i++) cin>>s[i]+1;

	int ans=bfs();
	cout<<ans<<endl;
}
signed main()
{
	
	int t;
	t=1;
	while(t--) work();

	return 0;
}

F

题意:给定N个字符串,每个字符串都是字符串 a b c d e f g h i j k l m n o p q r s t u v w x y z abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz的子序列,同时给定L,求出每次只用某一个字符串,每个字符可以用多次,可以形成多少种长度为L的字符串。

1 ≤ N ≤ 18 1\leq N \leq 18 1N18, 1 ≤ L ≤ 1 0 9 1 \leq L \leq 10^9 1L109.

首先考虑如何计算答案,对于某个字符串,它对答案的贡献为: 长度 L {长度}^L 长度L,然后把每个字符串的答案相加即可。但是如果我们这样计算会发现一个问题,某些字符串会含有相同的字符,因此会有答案被重复计算。

因此我们要求的是各个字符串的并集,根据容斥原理,我们只需根据公式看哪些答案需要减去,哪些答案加上即可。(也就是偶数个事件的交集要减去,奇数个事件的交集要加上)。

对于交集,也就是求它们的公共字符有几个。

code:

#include<bits/stdc++.h>

using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define x first
#define y second
typedef pair<int,int> pii;

//head
const int N=20,mod=998244353;
string s[N];

int qmi(int a,int k)
{
	int ans=1;
	while(k)
	{
		if(k&1) ans=ans*a%mod;
		a=a*a%mod;
		k>>=1;
	}
	return ans;
}
void work()
{
	int n,l;
	cin>>n>>l;
	for(int i=0;i<n;i++){
		cin>>s[i];
	}
	int ans=0;
	for(int i=0;i<(1<<n);i++){
		int cnt=__builtin_popcount(i);
		map<char,int> mp;
		int sum=0;
		for(int j=0;j<n;j++){
			if((i>>j)&1){
				for(auto ch:s[j]){
					mp[ch]++;
					if(mp[ch]==cnt) sum++;
				}
			}
		}
		if(cnt&1){//根据容斥原理,加上贡献
			ans=(ans+qmi(sum,l))%mod;
		}
		else {//减去贡献
			ans=(ans-qmi(sum,l)+mod)%mod;
		}
	}

	cout<<ans<<endl;
}

signed main()
{
	//ios;
	int t;
	t=1;
	//cin>>t;
	while(t--) work();

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值