Codeforces Round #842 (Div. 2)

A. Greatest Convex

 input

4

3

6

8

10

 output

2

5

7

9

题意:给定一个k,找出一个大于1小于k的数,让x!+(x-1)!为k的倍数

思路:令x=k-1,发现x!+(x-1)!=(k-1)!+(k-2)!=(k-1+1)*(k-2)!,故k-1即为答案

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=1e6+10;
int T,n;

int main()
{
    cin>>T;
    while(T--)
    {
		cin>>n;
		cout<<n-1<<endl; 
	}

	return 0;
}

B. Quick Sort

 input

4

3 2

1 2 3

3 1

3 1 2

4 2

1 3 2 4

4 2

2 3 1 4

 output

0

1

1

2

 题意:给定一个长度为n的排列,和一个整数k,有一种操作为选定k个不同的数,将他们删除后将这k个数的升序插入到排列最后,问最少操作几次使得序列有序

思路:由于要使得序列最终有序为1 2 3 4 5···,我们的操作选数是加在排列后面,所以1前面的数都要加在后面,然后1和2之间的数也要加在后面,同样2和3之间的,3和4之间的。如(7 1 5 2 4 3 6,1 2 3不用动,将7 5 4 6按一定顺序操作即可),其实就是求出一个给定序列中最长的从1开始的连续的上升子序列的长度L,用(n-L)除以k上取整即为答案。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=1e6+10;
int T,n,k,a[MAXN];
int main()
{
    cin>>T;
    while(T--)
    {
		cin>>n>>k;
		for(int i=1;i<=n;i++) scanf("%d",&a[i]);
		int t=1;
		for(int i=1;i<=n;i++) if(t==a[i]) t++;
		cout<<(n-t+k-1)/k<<endl;
	}

	return 0;
}

 C. Elemental Decompress

input

3

1

1

5

5 3 4 2 5

2

1 1

output

YES


YES
1 3 4 2 5 
5 2 3 1 4 
NO

 题意:给定一个长度为n的数组a,让构造出两个排列p、q,使得对于数组a的每个元素a[i],让max(p[i],q[i])=a[i],可以输出YES,并输出p和q,否则输出NO

思路:p和q都是从长度为n的排列,故相同的数最多只能出现两次,也就是说a数组中有三个数相同是非法的。

然后,先构造p,数组a去重后的数我们是可以直接给p的,如5 3 4 2 5,那么p首先可以填上4个数5 3 4 2 _,最后一个数不确定,我们填的规则是从小到大遍历a,如果出现不确定的位置,那么就从小到大填上第一个可填的数(不太好讲,看一遍代码就明白了),对于q的构造方法也一样,p中不确定的位置,q都是确定的。在填数的过程中,如果填的数比a[i]还大,就证明构造不出来。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=1e6+10;
struct node
{
	int num,t;
}a[MAXN];
int T,n,b[MAXN],c[MAXN];
bool cmp(node x,node y)
{
	return x.t<y.t;
}
int main()
{
    cin>>T;
    while(T--)
    {
		cin>>n;
		map<int,int> mp,mp1;
		bool f=0;
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i].t);
			a[i].num=i;
			b[i]=c[i]=0;
			if(mp.count(a[i].t)==0)
				b[i]=a[i].t;
			else if(mp1.count(a[i].t)==0)
			{
				c[i]=a[i].t;
				mp1[a[i].t]=1;
			}
			mp[a[i].t]++;
			if(mp[a[i].t]>=3) f=1;
		}
		if(f)
		{
			puts("NO");
			continue;
		}
		sort(a+1,a+n+1,cmp);
		int t=1;
		for(int i=1;i<=n;i++)
		{
			if(b[a[i].num]==0)
			{
				for(t;t<=n;t++)
				{
					if(mp.count(t)==0)
					{
						b[a[i].num]=t;
						if(t>a[i].t)
						{
							f=1;
							break;
						}
						mp[t]=1;
						break;
					}
				}
			}
		}
		if(f)
		{
			puts("NO");
			continue;
		}
		t=1;
		for(int i=1;i<=n;i++)
		{
			if(c[a[i].num]==0)
			{
				for(t;t<=n;t++)
				{
					if(mp1.count(t)==0)
					{
						c[a[i].num]=t;
						if(t>a[i].t)
						{
							f=1;
							break;
						}
						mp1[t]=1;
						break;
					}
				}
			}
		}
		if(f)
		{
			puts("NO");
			continue;
		}
		puts("YES");
		for(int i=1;i<=n;i++) printf("%d ",b[i]);
		puts("");
		for(int i=1;i<=n;i++) printf("%d ",c[i]);
		puts("");
	}

	return 0;
}

D. Lucky Permutation 

 input

4
2
2 1
2
1 2
4
3 4 1 2
4
2 4 3 1

output

0
1
3

 题意:给一个长度为n的排列,每次操作可以交换任意两个数,问最少操作多少次可以让这个排列的逆序对数仅为1 

思路:最终逆序对数仅为1的状态一定是其他数全有序只有两个相邻的数位置交换,如1 2 4 3 5,仅有3和4的位置交换。

将给定的排列看做标号,再给数组的下标一个标号,看做成图,如3 4 1 2就构成下面这个图,这个图有若干个环。

对于每个排列,一定可以构造成这样的图,然后统计每个环的长度,对于长度为1的环,代表其在自己位置上,不用变动。对于每个环,其长度设为k,那么一定可以通过k-1次操作让所有数换在自己的位置上,然后每个环都有序就将原序列变为有序了,此时只需要再花一次操作逆置两个连续的数即可,但若某个环中存在连续的数,那么可以通过k-2次操作使得这个环变为仅有一个逆序对,其他数有序的状态,就减少了一些操作,实现使用并查集判环即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=1e6+10;
int T,n,a[MAXN],f[MAXN],w[MAXN];
int find(int x)
{
	if(x==f[x]) return x;
	return f[x]=find(f[x]);
}
int main()
{
    cin>>T;
    while(T--)
    {
		cin>>n;
		for(int i=1;i<=n;i++) f[i]=i,w[i]=1;
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			int t1=find(a[i]),t2=find(i);
			if(t1!=t2)
			{
				f[t1]=t2;
				w[t2]+=w[t1];
			}
		}
		bool f2=0;
		for(int i=1;i<n;i++)
			if(find(i)==find(i+1)) f2=1;
		int cnt=0;
		for(int i=1;i<=n;i++)
		{
			if(f[i]==i&&w[i]>=2)
				cnt+=w[i]-1;
		}
		if(f2) printf("%d\n",cnt-1);
		else printf("%d\n",cnt+1);
	}
	return 0;
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值