Codeforces Round #842 (Div. 2) A ~ C 题解

文章介绍了三道编程竞赛题目,涉及最大整除条件、序列排序操作次数的最小化以及构造排列满足特定条件的问题。通过分析思路和代码实现,展示了如何解决这些问题。
摘要由CSDN通过智能技术生成

A. Greatest Convex

题意

题目链接
找出最大的 x x x,其中 1 ≤ x < k 1≤x<k 1x<k,使得 x ! + ( x − 1 ) ! x!+(x−1)! x!+(x1)!能整除 k k k

思路

x = k − 1 x=k-1 x=k1,则 ( k − 1 ) ! + ( k − 2 ) ! = ( k − 1 ) ( k − 2 ) ! + ( k − 2 ) ! = k ( k − 2 ) ! (k-1)!+(k−2)!=(k-1)(k−2)!+(k−2)!=k(k-2)! (k1)!+(k2)!=(k1)(k2)!+(k2)!=k(k2)!,当 k ≥ 2 k≥2 k2时,输出 k − 1 k-1 k1即可。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn = 32768 + 5;
ll k,t;
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>t;
	while(t--){
		cin>>k;
		cout<<k-1<<endl;
	}
    return 0;
}

B. Quick Sort

题意

题目链接
给出一个排列和一个整数 k k k,每次操作中,你可以从排列中取出 k k k个不同的元素,将它们从小到大排序后放到排列末尾。求使得排列从小到大排好序的最小操作数。

思路

统计排列中没有按照从小到大排好序的元素的个数,即需要被操作的元素最少有多少个。因为每次操作 k k k个,那么答案就是这些元素的数量除以 k k k再向上取整。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn = 1e5 + 5;
ll t, p[maxn];
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>t;
	while(t--){
		ll n,k;
		cin>>n>>k;
		for(ll i=1;i<=n;i++)cin>>p[i];
		ll cur = 1, ans = 0;
		for(ll i=1;i<=n;i++){
			if(p[i]==cur)cur++;
			else ans++;
		}
		ans = ceil(ans*1.0/k);
		cout<<ans<<endl;
	}
    return 0;
}

C. Elemental Decompress

题意

题目链接
给出一个数组 a a a,请你构造两个排列 p p p q q q,使得对于每一个 1 ≤ i ≤ n 1≤i≤n 1in,都有 m a x ( p i , q i ) = a i max(p_{i},q_{i})=a_{i} max(pi,qi)=ai

思路

首先,这个数组中不可能超过2次出现同一个数,否则就构造不出来。

我们可以对数组进行结构体排序,从小到大分析每一个元素对应的 p i p_{i} pi q i q_{i} qi

如果数组中的这个元素出现了2次,那么就分别为两个排列的相应位置添上这个元素;如果数组中的这个元素只出现了1次,那么可以把这个元素对应的 p i p_{i} pi q i q_{i} qi都赋成这个元素自己,那么这个位置的最大值就一定是这个元素。

最后,考虑两个排列中还没有被赋值过的位置,一定是数组中元素出现2次的位置,一个排列已经赋值,另一个排列在这个位置还没有赋值。

我们对两个排列分别从小到大添数。因为之前已经对数组元素从小到大排好序了,那么现在只需要从小到大地看,每个排列中还有哪个数没有用,这个步骤我们之前可以开两个 v i s vis vis数组记录。

如果这个数可以用,再看当前这个可以用的最小的数是否比另一个排列中这个位置的元素小,如果是,那就添上这个数,再记录这个数已经用过了。

如果不满足,就一直从小到大查找,直到找到为止,类似于单指针。如果一直找不到,就说明构造不出来。这样遍历添数的时间复杂度仍然是线性的。

最后输出的时候,需要用一个 p o s pos pos数组记录对应的下标映射关系,表示原来 a [ i ] . i d a[i].id a[i].id这个位置上的数对应的在两个排列中的下标是 i i i。那么 p o s [ i ] pos[i] pos[i]就记录的是数组没有排序时 i i i从小到大对应的在两个排列中的下标。这样就可以正确输出了。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn = 2e5 + 5;
ll t;
struct node{
	ll x, id;
	bool friend operator < (const node A, const node B){
		return A.x < B.x;
	}
}a[maxn];
ll p[maxn],q[maxn],cnt[maxn],pos[maxn];
bool visp[maxn],visq[maxn];
int main(){
	//ios::sync_with_stdio(0);
	//cin.tie(0),cout.tie(0);
	cin>>t;
	while(t--){
		ll n;
		cin>>n;
		memset(a,0,sizeof(0));
		for(ll i=1;i<=n;i++)visp[i]=false,visq[i]=false,cnt[i]=0;
		for(ll i=1;i<=n;i++)p[i]=0,q[i]=0,a[i].x=0,a[i].id=0,pos[i]=0;
		for(ll i=1;i<=n;i++)cin>>a[i].x,a[i].id=i;
		sort(a+1,a+1+n);
		for(ll i=1;i<=n;i++){
			cnt[a[i].x]++;
		}
		bool flag=true;
		for(ll i=1;i<=n;i++){
			if(cnt[a[i].x]>2){
			    flag=false;
			}
		}
		if(flag==false){
		    cout<<"NO"<<endl;
			continue;
		}
		for(ll i=1;i<=n;i++){
			if(cnt[a[i].x]==2){
				if(visp[a[i].x]==false)p[i]=a[i].x,visp[p[i]]=true;
				else q[i]=a[i].x,visq[q[i]]=true;
			}
		}
		for(ll i=1;i<=n;i++){
			if(cnt[a[i].x]==1){
				p[i]=a[i].x;
				q[i]=a[i].x;
				visp[p[i]]=true;
				visq[q[i]]=true;
			}
		}
		ll curq = 1, curp = 1;
		bool f=true;
		for(ll i=1;i<=n;i++){
			if(p[i]&&q[i]==0){
				while(visq[curq]==true||curq>p[i]){
					curq++;
					if(curq==n+1)break;
				}
				if(curq==n+1){
					f=false;break;
				}
				q[i]=curq;
				visq[curq]=true;
			}
			else if(p[i]==0&&q[i]){
				while(visp[curp]==true||curp>q[i]){
					curp++;
					if(curp==n+1)break;
				}
				if(curp==n+1){
					f=false;break;
				}
				p[i]=curp;
				visp[curp]=true;
			}
		}
		if(f==false){
		    cout<<"NO"<<endl;
		}
		else{
			cout<<"YES"<<endl;
			for(ll i=1;i<=n;i++){
				pos[a[i].id]=i;
			}
			for(ll i=1;i<=n;i++){
				cout<<p[pos[i]]<<" ";
			}
			cout<<endl;
			for(ll i=1;i<=n;i++){
				cout<<q[pos[i]]<<" ";
			}
			cout<<endl;
		}
	}
    return 0;
}

心得

非常遗憾,本来C题赛中我是可以做出来的,最后输出那里调了一会儿没调对,结果比赛就结束了。赛后只用了一会儿就调完了,一交上去就AC了,可以说是非常难受,就差了一点点。以后还是要注意细节。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

keguaiguai

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值