Codeforces Round 1015, Div. 1 + Div. 2

2025.4.14 Div1+2

Teza Round 1 (Codeforces Round 1015, Div. 1 + Div. 2)

A. Max and Mod(gcd,mod,排列)

题意

您将得到一个整数n 。找出长度为 n的任意排列p ,使得:

-对于所有 2≤i≤n ,满足 max(pi−1,pi)mod i i i = i−1

思路

易知,当 n 为奇数时

n 1 2 3 4…n-1

这种排列方式,恰好可以满足取模要求。

n 为偶数时呢?

可以大胆猜测 -1

证明:

设n 放在位置 x ,则 max(a[x-1],a[x])=n,应满足 n%x=x-1

若x 为奇数 ,偶数%奇数=奇数,不合题意

若x 为偶数,偶数%偶数=偶数,不合题意

代码


B. MIN = GCD

题意

给定一个长度为 n n n 的正整数序列 a a a 。确定是否可以重新排列 a a a ,使得存在整数 i i i 1 ≤ i < n 1 \le i \lt n 1i<n )满足

min ⁡ ( [ a 1 , a 2 , … , a i ] ) = gcd ⁡ ( [ a i + 1 , a i + 2 , … , a n ] ) . \min([a_1,a_2,\ldots,a_i])=\gcd([a_{i+1},a_{i+2},\ldots,a_n]). min([a1,a2,,ai])=gcd([ai+1,ai+2,,an]).

思路

min, 排列

  • 此时可以想到,当数组升序排列时,min( )为定值即 a[ 1 ]
  1. 如果将min放在 i 之前,那么关于 a[ i ~n ]的数字就可以随意选择。

​ 因为放前面并不影响 min ,又因为 min 为最小值,要想保证 gcd=min,

​ 只需要把 min的倍数进行取模判断是否可以得到gcd=min

  1. 如果将 min ,放在右边呢?

    那么此时gcd一定小于等于 min ,而左边一定大于min

    因此,这种情况排除

代码

void solve()
{
    int n;
    cin>>n;
    fir(i,1,n)
    cin>>a[i];
    sort(a+1,a+n+1);
    int f=0,k=0;
    fir(i,2,n)
    {
        if(a[i]%a[1]==0)
        {
            if(f==1)
                k=__gcd(k,a[i]);
           else
               f=1,k=a[i];   
            if(k==a[1])
            {
                cout<<"Yes\n";
                return;
            }    
        }
    }
    cout<<"No\n";
}

C. You Soared Afar With Grace(交换)

题意

给出一个长度为 n n n 的排列 a和b 。您最多可以执行 n n n 次以下操作:

-选择两个索引 i i i j j j ( 1 ≤ i , j ≤ n 1 \le i, j \le n 1i,jn i ≠ j i \ne j i=j )

a i a_i ai a j a_j aj 交换,将 b i b_i bi b j b_j bj 交换。操作后判断 a a a b b b 是否可以互逆。

如果可能,输出任何有效的操作序列。否则,输出 − 1 -1 1

长度为 n n n 的排列是由 n n n 个从 1 1 1 n n n 的不同整数以任意顺序组成的数组

思路

赛时没过,看的题解,首先给出两个提示:

  1. 无论怎么交换,a[ i ] 和b[ i ] 的对应关系会变吗 ?
  2. 与a[ i ] 和 b [ i ] 颠倒的一对, 应该在什么位置呢?

这时你肯定发现

对于 1 ,对应关系是不会变的

对于 2 ,应该放在 n-i+1的位置

所以,思路就有了:

用一个数组用来记录a中每个数的位置,从前往后遍历。

遇到以下三种情况输出-1:

  • n为偶数,a[ i ]=b[ i ]
  • 出现两个及以上 a[ i ]=b[ i ]
  • a[ i ]和b[ i ] 不相互对应

代码

int a[N],b[N],c[N];
void solve()
{
	int n,f=0;
	cin>>n;
	vector<PII> v;
	fir(i,1,n)
	{
	  cin>>a[i];
	  c[a[i]]=i;
	}
	fir(i,1,n) 
	cin>>b[i]; 
	fir(i,1,n/2)
	{
		if(a[i]==b[i])
		{
			if(n%2==0||f==1)//没有合适位置摆放
			{
				cout<<"-1\n";
				return;
			}
			else if(i!=n/2+1)
			{
				swap(a[i],a[n/2+1]);
				swap(b[i],b[n/2+1]);
				swap(c[a[i]],c[a[n/2+1]]);
				v.push_back({i,n/2+1});
				i--;
			}
			f=1;
		}
		else if(b[c[b[i]]]!=a[i])//并没有两两对应
		{
			cout<<"-1\n";
				return;
		}
		else
		{
			if(c[b[i]]!=n-i+1)
			{
				int p=c[b[i]],q=n-i+1;
				swap(a[q],a[p]);
				swap(b[q],b[p]);
				swap(c[a[p]],c[a[q]]);
				v.push_back({q,p});	
			}
		}
	}
	cout<<v.size()<<'\n';
	for(auto it: v)
	{
		int x=it.fi,y=it.se;
		cout<<x<<' '<<y<<'\n';
	}
}

D. Arcology On Permafrost(mex,构造)

题意

给你三个整数 n n n 、 m 和 k k k ,其中 m ⋅ k < n m \cdot k \lt n mk<n

对于由非负整数组成的序列 b b b ,定义 f ( b ) f(b) f(b)如下:

  • 你可以对 b b b 进行如下操作:

    l l l表示 b b b 的当前长度。选择一个正整数 1 ≤ i ≤ l − k + 1 1 \leq i \leq l - k + 1 1ilk+1 ,删除索引 i i i i + k − 1 i + k - 1 i+k1 的子数组,并将剩余部分连接起来。

  • f ( b ) f(b) f(b) 被定义为执行上述操作 最多 m m m 次(可能为零)后 mex ⁡ ( b ) \operatorname{mex}(b) mex(b)的 最小可能值。

你需要构造一个长度为 a a a 的序列,该序列由非负整数 $n组成,使得:

  • 对于所有 1 ≤ i ≤ n 1 \le i \le n 1in , 0 ≤ a i ≤ 1 0 9 0 \le a_i \le 10^9 0ai109.

  • 在所有这样的序列 a a a中, f ( a ) f(a) f(a)最大的

    整数集合 的最小排除数(MEX)定义为在集合 c c c 中不出现的最小非负整数 x x x

思路

f (b) 是 mex(b) 的最小可能值,所以最多进行m次操作,视作 进行 m 次。

  • 设 f (b) 最大是 x , 那么应该怎么排列呢?

由删除操作,模拟可得,以下这种排列才是最优:

0 1 2 3 … x-1 0 1 2 3 … x-1 0 1 …

这样连续重复的排列,才能使 不断删除 也总会留下 0 ~ x-1

  • 那 x 应该是多少呢?

每次删除 k 个 ,删除m 次,最后剩下 n-m * k

如果 n-m*k < k ,这时候就可以以 i%k 进行排列, f (b)最大为 n - m * k

如果 n-m*k >= k ,这时候我们可以换一种更优排列:

​ 每个数字最多删除m 次,要想留下来,至少存在 m+1 个。

​ 每个数字出现 m+1 次,会有多少种呢?

​ n / (m+1) ,所以 f (b) 最大为 n / (m+1)

下图例子,帮助你理解:

请添加图片描述

代码

#include <bits/stdc++.h>
using namespace std;

int main() {
	int T;
	cin >> T;
	while (T--) {
		int n, m, k;
		cin >> n >> m >> k;
		if(n-m*k<k)
		for(int i=0;i<n;i++)
		cout<<i%k<<' ';
		else
		for(int i=0;i<n;i++)
		cout<<i%(n/(m+1))<<' ';
		cout<<"\n";
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值