Codeforces Round #679 (Div. 2, based on Technocup 2021 Elimination Round 1)

本文提供CodeForces竞赛1413题目的解析与代码实现,涵盖构造序列、矩阵恢复、最小差值计算、操作序列验证及最大血量估算等问题。

题库链接:https://codeforces.com/contest/1413
A. Finding Sasuke
题意:给你一个序列a(长度为n,且n为偶数),让你构造一个序列b,使得 a 1 ∗ b 1 + a 2 ∗ b 2 + . . . + a n ∗ b n = 0 ∣ a i ∣ < 100 , ∣ b i ∣ < 100 且 不 为 零 a_1*b_1+a_2*b_2+...+a_n*b_n=0 \\|a_i|<100,|b_i|<100 且不为零 a1b1+a2b2+...+anbn=0ai<100,bi<100
思路:暴力寻找,每两个为一组
代码:

int main(){
	int t;
	cin>>t;
	while(t--){
		int n;
		cin>>n;
		int a[120],b[120];
		for(int i=1;i<=n;i++){
			cin>>a[i];
		}
		for(int i=1;i<=n;i+=2){
			int flag=0;
			for(int j=-100;j<=100;j++){
				for(int k=-100;k<=100;k++){
					if(k!=0&&j!=0){
						if(j*a[i]+k*a[i+1]==0){
							b[i]=j;
							b[i+1]=k;
							flag=1;
							break;
						}
					}
				}
				if(flag==1)
				break;
			}
		}
		for(int i=1;i<=n;i++){
			cout<<b[i]<<" ";
		}
		cout<<endl;
	}
} 

B. A New Technique
题意:恢复矩阵,给你矩阵的行,列,顺序是打乱的,保证答案唯一
思路:根据给出的列可以找到代码的第一行,然后根据第一行恢复列的矩阵
第一个矩阵是行变化而列不变的矩阵
第二个矩阵是列变化而行不变的矩阵
故需要先找到原来矩阵中第一行第一列的元素 u[i][1] == v[j][1]
就知道了这个元素的行列,故可以从这个元素出发进行操作对每一列进行赋值,一列一列的赋值
代码:

struct node{
	int a[550];
	int id;
}str[550];
int b[550][550];
 
int cmp(node a,node b){
	return a.id<b.id;
}
 
int main(){
	int t;
	cin>>t;
	while(t--){
		int n,m;
		cin>>n>>m;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				cin>>str[i].a[j];
			}
		}
		for(int i=1;i<=m;i++){
			for(int j=1;j<=n;j++){
				cin>>b[i][j];
			}
		}
		int x;int flag=0;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				if(b[j][1]==str[i].a[1]){
					x=j;flag=1;
					break;
				}
			} 
			if(flag==1)
			break;
		}
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				if(b[x][i]==str[j].a[1]){
					str[j].id=i;
					break;
				}
			}
		}
		sort(str+1,str+1+n,cmp);
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				cout<<str[i].a[j]<<" ";
			}
			cout<<endl;
		}
	}
}

C. Perform Easily
题意:
输入,a1–a6。然后n,接着b1–bn。
问每个b1减去一个a之后的最小的(最大值-最小值)
思路:
我们将所有对(bj−ai,j)按字典顺序排序。现在我们需要找到一个子段,它的最小范围包含第一个字段,并且使从1到n的所有数字都出现在第二个字段中(所以这意味着每个音符至少有一个字符串fret组合)。
对于每个l,表示最小右(l),使[l,right(l)]是有效的子集。很容易看出right(l)≤right(l+1),因为如果[l+1,right(l+1)]包含第二个字段中从1到n的所有数字,那么[l,right(l+1)]也是如此。所以要找到好的(l),你可以使用两个指针,保持在片段上出现的一组音符。
一旦我们计算了它,我们只需打印出所有可能段[l,right(l)]端点的第一个字段之间的最小差异。最后的复杂度是O(nmlog(nm))。
代码:

int a[10],b[maxn];

struct node{
	int num;
	int id;
}str[maxn*6];

int cmp(node a,node b){
	return a.num<b.num;
}
int st[maxn*6];

int cnt=0;

int main(){
	for(int i=1;i<=6;i++)
	scanf("%d",&a[i]);
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
	scanf("%lld",&b[i]);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=6;j++){
			str[++cnt].id=i;
			str[cnt].num=b[i]-a[j];
		}
	}
	sort(str+1,str+1+cnt,cmp);
	int r=1,num=0;
	int ans=inf;
	for(int i=1;i<=cnt;i++){
		while(r<=cnt&&num<n){
			st[str[r].id]++;
			if(st[str[r].id]==1)num++;
			r++;
		}
		if(num==n)ans=min(ans,str[r-1].num-str[i].num);
		st[str[i].id]--;if(st[str[i].id]==0)num--;
	}
	printf("%d\n",ans);
}

D. Shurikens
题意:
一家店卖忍者用的手里剑(应该是手里剑)。每天有n把手里剑,每把剑的价格分别为1~n。店员不是一次性把所有手里剑都摆在柜台上,而是每次摆一把,分n次摆。我们不知道摆剑的顺序。
有忍者来买手里剑,他们每次只买当前摆在柜台上的手里剑中最便宜的一把。
问我们:题目中给出的2n个操作,是不是合理的。如果不合理,输出"NO";如果合理,输出"YES",并另起一行输出一行数列,表示店员放置手里剑的顺序(所有可能中的一种即可)。
关于操作的定义:
"+“表示,店员向柜台放了一把手里剑,至于放的是哪把我们不知道。
“- x"表示有忍者买走了价值为x的手里剑。
关于操作合理的定义:
当柜台为空时,忍者仍买走了一把手里剑,显然是不合理的。
当不能满足所有忍者都购买了当前柜台中最便宜的一把手里剑时,显然也不合理。
思路:
我们可以确定的是,忍者只会买当前柜台中最便宜的一把手里剑。
也就是说当我们遇到”- x"操作时,x必然是当前柜台中最便宜的一把手里剑的价格,即当前柜台中的其他手里剑的价格都比x大。
但是有一个问题,我们根本没有办法确定,当前柜台中价格最低的手里剑是哪一把。因为”+"操作只能告诉我们,店员向柜台中放置了一把手里剑,但是没有告诉我们他放置的手里剑的价格。
那该怎么办呢?有一个想法是,我们根据顾客买的手里剑的价格来确定当前柜台中的手里剑的价格。
所以我们制定一个对我们最有利的规则:忍者买的手里剑总是柜台所有手里剑中最新拿上来的一把。也就是说,最新拿上来的一把就是当前柜台中价格最低的一把。再换句话说,柜员向柜台中摆剑的时候,一定要保证新摆的剑比当前柜台中所有的剑的价格都低!
那有朋友该纳闷了,店员凭啥这么摆啊?咱不是不知道店员摆的顺序吗?
我们之所以这么摆,是因为这样对我们最有利!
正如我们所知道的,我们不知道店员摆手里剑的顺序。我们能做的只能是根据顾客买手里剑的顺序来反推操作是否可行。也就是说,只要对我们最有利的情况是可行的,那这个操作就是可行的。
那我们制定的规则最有利的依据是什么呢?我们在这道题中的限制就是,忍者只会买当前柜台中价格最低的一把手里剑。作为一名奸商 有智慧的商人,我摆的时候顺序一定会是n,n-1,……1.因为你不是总买最便宜的吗,那也就意味着越便宜的手里剑所受的限制就越少,那我当然是先把限制大的给卖出去。
但是我们能不能做到整个过程按照降序摆呢?不能。因为忍者买的时候给出了价格x,也就是说我们要想让操作合理,那就必须保证价格为x的我们已经摆进去了。别等着人家买了个价格为2的,你降序摆才摆到价格为4的。那对于我们来说最优解就是:**先摆贵的,并保证刚好在忍者要买价格为x的手里剑之前,把价值为x的手里剑摆上去。**我刚好在你要买之前把你想要的这个给摆上去。这就是我们的最有利的规则。相信大家也品出味了,刚摆的手里剑是最便宜的,而顾客只买最便宜的。也就是说,越先摆进柜台的就越晚出去。先进后出,这不就是个栈吗!现在写代码就很容易了。
代码

stack<int>v;
int a[maxn];
 
int main(){
	int n;
	int cnt1=0,cnt2=0;
	cin>>n;
	char op[3];
	int num=0,x;
	for(int i=1;i<=2*n;i++){
		cin>>op;
		if(op[0]=='+'){
			v.push(++num);
		}
		else{
			cin>>x;
			if(v.empty()){
				puts("NO");
				return 0;
			}
			else if(x<a[v.top()+1]){
				puts("NO");
				return 0;
			}
			a[v.top()]=x;
			v.pop();
		}
	}
	cout<<"YES"<<endl;
	for(int i=1;i<=n;i++){
		cout<<a[i]<<" ";
	}
}

E. Solo mid Oracle
题意:你有一个技能,每次使用可以使敌方英雄的血量减少a,并在c秒内每秒回复b点生命值,技能的冷却时间为d。问在你能击败的敌方英雄的血量最大为多少。
思路:
在这里插入图片描述
代码:

int main(){
	int t;
	cin>>t;
	while(t--){
		ll a,b,c,d,ans=0;
		scanf("%lld%lld%lld%lld",&a,&b,&c,&d);
		if(a>b*c){
			puts("-1");
		}
		else{
			ll tmp=(c/d);
			ll tmp2=(a/(b*d));
			tmp=min(tmp,tmp2);
			ans=(tmp+1)*a-(tmp*(tmp+1)/2)*b*d;
			printf("%lld\n",ans);
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值