AtCoder Beginner Contest 308 题解(C~F)

AtCoder Beginner Contest 308

C

题意:结构体排序,按 A i A i + B i \frac {A_i}{A_i+B_i} Ai+BiAi 从大到小来排,若相同则按序号较小的在前来排。

重载一下小于号即可。

code:

const int N=2e5+10,mod=998244353;
struct node{
	int a,b,id;
 
	bool operator <(const node &t) const{
		if(t.a*(a+b)==a*(t.a+t.b)) return id<t.id;
		return a*(t.a+t.b)>t.a*(a+b);
	}
}a[N];
void work()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		int x,y;
		cin>>x>>y;
		a[i]={x,y,i};
	}
	sort(a+1,a+1+n);
	for(int i=1;i<=n;i++) cout<<a[i].id<<" ";
	
}

D

题意:给定一个二维字符数组,问是否能够从(1,1)到达(n,m),且经过的路径必须为 “snukesnuke…"(可以不完整) 。

考虑使用bfs,因为题目要求的是路径是否存在,因此我们需要记录一下到当前这个点的距离为多少,然后判断是否合法即可。

code:

string s[N];
int dx[]={0,1,0,-1},dy[]={1,0,-1,0};
int n,m;
string ss="snuke";
bool st[N][N];
int d[N][N];
bool bfs()
{
	queue<pii> q;
	q.push({0,0});
	st[0][0]=1;
	
	while(q.size())
	{
		auto t=q.front(); q.pop();
		if(t.x==n-1&&t.y==m-1){
			return 1;
		}
		for(int i=0;i<4;i++){
			int a=t.x+dx[i],b=t.y+dy[i];
			int cnt=d[t.x][t.y];
			if(a<0||a>=n||b<0||b>=m||s[a][b]!=ss[(cnt+1)%5]||st[a][b]) continue;
			q.push({a,b});
			st[a][b]=1;
			d[a][b]=d[t.x][t.y]+1;
		}
	}
	return 0;
}
void work()
{
	cin>>n>>m;
	for(int i=0;i<n;i++){
		cin>>s[i];
	}
	if(s[0][0]!='s') {
		cout<<"No"<<endl;
		return ;
	}
	if(bfs()) cout<<"Yes"<<endl;
	else cout<<"No"<<endl;
}

E

题意:给定字符串,只包括"M",“E”,“X”,其值为 a i a_i ai ,问序列中所有 “MEX” 子序列,其mex的值的和是多少。

很经典的一个处理方法,但是我竟然忘了,果然要康复,我们考虑中间字符“E” ,对于每个字符“E” ,我们只要统计它前面有几个“M”,以及它后面有几个“X”,然后计算一下贡献即可。

code:

const int N=3e5+10,mod=998244353;

string s;
int n,m;
int a[N];
int b[N][3],c[N][3];
void work()
{
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];

	cin>>s;
	s=" "+s;
	for(int i=n;i>=1;i--){
        for(int j=0;j<3;j++){
            if(j==a[i]&&s[i]=='X') c[i][j]=c[i+1][j]+1;
            else c[i][j]=c[i+1][j];
        }
	}
	for(int i=1;i<=n;i++){
        for(int j=0;j<3;j++){
            if(j==a[i]&&s[i]=='M') b[i][j]=b[i-1][j]+1;
            else b[i][j]=b[i-1][j];
        }
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		//cout<<c[1][2]<<endl;
		if(s[i]=='E'){
			for(int j=0;j<3;j++){
				for(int k=0;k<3;k++){
					for(int l=0;l<4;l++){
						if(l!=j&&l!=k&&l!=a[i]) {
							ans+=l*b[i][j]*c[i][k];
							break;
						}
					}
				}
			}
		}
	}
	cout<<ans<<endl;
}

F

题意:有n个物品,同时你有m张优惠券,但是每张优惠券有限制,即价值超过 L i L_i Li的物品才能使用,然后优惠 D i D_i Di元,保证 D i ≤ L i D_i \leq L_i DiLi, 且每个物品最多使用一张优惠券,问买下这n个物品最低需要多少钱。

考虑贪心的策略,即是先选价值较大的还是选价值较小的,容易发现从价值较小的物品选起是正确的,因为后面价值较大的物品不会影响前面的物品,因为优惠劵有限制。相反如果我们先优先选取价值较大的,则可能暂时没有优惠券使用,而后续又出现了优惠券的情况。

因此我们只需对物品按价值从小到大排序,然后对优惠券也排个序,满足条件的优惠券都插入到优先队列中去,然后每次取堆顶的优惠券来计算答案即可。

code:


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

int n,m;
int a[N];
int v1[N],v2[N];
pii b[N];

void work()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=m;i++){
		cin>>b[i].x;
	}
	for(int i=1;i<=m;i++){
		cin>>b[i].y;
	}
	sort(b+1,b+1+m);
	
	for(int i=1;i<=m;i++){
		v1[i]=b[i].x;
		v2[i]=b[i].y;
	}
	int ans=0;
	sort(a+1,a+1+n);
	
	priority_queue<int> q;
	for(int i=1,j=1;i<=n;i++){
		while(v1[j]<=a[i]&&j<=m){
			q.push(v2[j]);
			j++;
		}
		if(q.size()){
			ans-=q.top();
			q.pop();
		}
	}
	for(int i=1;i<=n;i++) ans+=a[i];
	cout<<ans<<endl;	
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值