文章目录
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 1≤i<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 ]
- 如果将min放在 i 之前,那么关于 a[ i ~n ]的数字就可以随意选择。
因为放前面并不影响 min ,又因为 min 为最小值,要想保证 gcd=min,
只需要把 min的倍数进行取模判断是否可以得到gcd=min
-
如果将 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 1≤i,j≤n , 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 的不同整数以任意顺序组成的数组
思路
赛时没过,看的题解,首先给出两个提示:
- 无论怎么交换,a[ i ] 和b[ i ] 的对应关系会变吗 ?
- 与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 m⋅k<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 1≤i≤l−k+1 ,删除索引 i i i 至 i + k − 1 i + k - 1 i+k−1 的子数组,并将剩余部分连接起来。
-
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 1≤i≤n , 0 ≤ a i ≤ 1 0 9 0 \le a_i \le 10^9 0≤ai≤109.
-
在所有这样的序列 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;
}