ABC331题解(A~E)

[ABC331A] Tomorrow

题意简述

在 ATC 王国,一年有 M M M 月,一月有 D D D 天。若今天的日期是 a a a b b b c c c 天,明天的日期是多少?

代码示例

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,a,b,c,x,y;
signed main(){
	cin>>x>>y>>a>>b>>c;
	if(b==x&&c==y) b=1,c=1,a++;
	else if(c==y) c=1,b++;
	else c++;
	cout<<a<<" "<<b<<" "<<c<<endl; 
	return 0;
}

[ABC331B] Buy One Carton of Milk

题意简述

6 6 6 个鸡蛋需要 a a a 元,买 8 8 8 个鸡蛋需要 b b b 元,买 12 12 12 个鸡蛋需要 c c c 元,现在需要至少 n n n 个鸡蛋,最少需要花多少钱?

解题思路

n n n 极小,只有 100 100 100,所以直接枚举即可。

代码示例

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,a,b,c,ans=0x3f3f3f3f3f3f3f3f;
signed main(){
	cin>>n>>a>>b>>c;
	for(int i=0;i<=n;i++){
		for(int j=0;j<=100;j++){
			for(int k=0;k<=100;k++){
				if(i*6+j*8+k*12>=n) ans=min(ans,i*a+j*b+k*c);
			}
		}
	}
	cout<<ans<<endl;
	return 0;
}

[ABC331C] Sum of Numbers Greater Than Me

题意简述

给定 n n n 个数,对于每个 i i i,输出所有大于 a i a_i ai 的数的和。

做题思路

a a a 数组复制到 c n t cnt cnt 中,然后对 c n t cnt cnt 进行排序,用 s u m sum sum c n t cnt cnt 求前缀和。对于每个 a i a_i ai,二分找它在 c n t cnt cnt 中的位置,然后用前缀和相减得出答案。

代码示例

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,a[200010],sum[200010],cnt[200010];
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i],sum[i]=a[i],cnt[i]=a[i];
	sort(cnt+1,cnt+n+1);
	for(int i=1;i<=n;i++) sum[i]=sum[i-1]+cnt[i];
	for(int i=1;i<=n;i++){
		int id=upper_bound(cnt+1,cnt+n+1,a[i])-cnt;
		id--;
		cout<<sum[n]-sum[id]<<" ";
	}
	return 0;
}

[ABC331D] Tile Pattern

题意简述

给定一个由黑白格子构成的矩形,然后将这个矩形复制无数多次,相互对齐拼接起来,得到一个无限大平面。再给定 q q q 次询问,每次询问给定一个左上角和右下角,求这个矩形范围内有多少个黑格。

解题思路

首先对给定的矩形做一个二维前缀和,方便直接算出来答案。

一种方法是利用容斥。

定义 s u m ( x , y ) sum(x,y) sum(x,y) ( 1 , 1 ) (1,1) (1,1) ( x , y ) (x,y) (x,y) 的范围内的黑格数量。答案显然就是和二维前缀和一样的容斥。但是注意询问的范围可能很大,所以需要算一下其中包含了多少个基准矩形,再乘上基准矩形的答案。

另一种方法是我的赛时做法,非常愚蠢,还把横纵搞反了导致我调了二十分钟。我们直接把基准矩形定义为以询问的左上角为顶点的 n × n n \times n n×n矩形,然后跟上面一样算就可以。

思考一下这种方法的本质是什么。是以询问的左上角为基准,重新找到一个基准矩形,重新构造一个无限大平面,那么构造完了之后询问的矩形很显然就位于 ( 1 , 1 ) (1,1) (1,1) 这个点了。

下面的代码是我的赛时解法。

代码示例

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,q,xxxx[20][20]={0},sum[2010][2010];
//小寄巧:xxxx数组是为了防止sum越界的。
//底下的for循环会遍历到sum[-1][-1]
//但是sum前面放上一个数组就不会RE
string s[2010];
int getans(int x,int y,int l1,int l2){
//x,y是左上角位置,l1,l2是长和宽
	if(l1==0||l2==0) return 0;
	return sum[x+l1-1][y+l2-1]-sum[x-1][y+l2-1]-sum[x+l1-1][y-1]+sum[x-1][y-1];
}
signed main(){
	cin>>n>>q;
	for(int i=0;i<n;i++) cin>>s[i],s[i]+=s[i];
	for(int i=n;i<2*n;i++) s[i]=s[i-n];
	for(int i=0;i<2*n;i++){
		for(int j=0;j<2*n;j++) sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+(s[i][j]=='B');
	}
    //处理 (2*n)*(2*n)的矩形答案,方便计算
	while(q--){
		int a,b,c,d,ans=0;
		cin>>a>>b>>c>>d;
		c-=a/n*n;
		d-=b/n*n;
		b%=n;
		a%=n;
		ans+=((c-a+1)/n)*((d-b+1)/n)*(getans(a,b,n,n));
		ans+=((d-b+1)/n)*(getans(a,b,(c-a+1)%n,n));
		ans+=((c-a+1)/n)*(getans(a,b,n,(d-b+1)%n));
		ans+=getans(a,b,(c-a+1)%n,(d-b+1)%n);
		cout<<ans<<endl;
	}
	return 0;
}

[ABC331E] Set Meal

题意简述

n n n 道主菜,第 i i i 道价格为 a i a_i ai。有 m m m 道小菜,第 i i i 道价格为 b i b_i bi。定义一个套餐为一份主菜加一份小菜。给定 m m m 种不可行的套餐,求出剩余套餐中最贵的要花多少钱。

解题思路

不可行的套餐总数只有 m m m,不会太多,所以遍历每一道主菜,遍历的时候我们按以下三步走:

对于这道主菜而言不可行的小菜删掉。

统计答案。

将删掉的小菜加回去。

将所有小菜丢到一个可重集里,执行以上操作就可以了。

代码示例

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,l,a[2000010],b[2000010],c[2000010],d[2000010],ans=0;
multiset<int> s;
vector<int> G[2000010];
signed main(){
	cin>>n>>m>>l;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=m;i++) cin>>b[i],s.insert(b[i]);
	for(int i=1;i<=l;i++) cin>>c[i]>>d[i],G[c[i]].push_back(d[i]);
	for(int i=1;i<=n;i++){
		for(int v:G[i]) s.erase(s.find(b[v]));
		if(!s.empty()) ans=max(ans,a[i]+*s.rbegin());
        //不判非空=RE一发 :(
		for(int v:G[i]) s.insert(b[v]);
	}
	cout<<ans<<endl;
	return 0;
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值