A 题目链接:https://codeforces.com/contest/1768/problem/A

input:
4
3
6
8
10
output:
2
5
7
9
题意:
给定一个整数 k ,找到一个整数 x ,使 x ! + (x − 1) ! 是 k 的倍数 ,输出这个 x ,如果找不到,输出-1.
思路:
x ! + (x − 1) ! = (x + 1) * ( x - 1) ! , 令 x + 1 = k ,即 x = k - 1
代码如下:
#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define endl "\n"
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int N=2e5+10;
ll t,n;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--)
{
cin>>n;
cout<<n-1<<endl;
}
return 0;
}
B 题目链接:https://codeforces.com/contest/1768/problem/B

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 的排列 p,给定一个数 k。给定一种操作:从排列 p 中选择 k 个数,按从小到大的顺序放到排列的末尾。问最少需要多少次操作,使得排列 p 变为从小到大的排列。
思路:
思路比较简单,找到所有不需要拿出来的数,剩下的数都是需要被操作的。
如何判断哪些数不需要操作呢?通俗来讲就是直接从原排列中找到1 ,2,3……排列即可,这些有序的数不需要被操作。说的不是很明白,具体见代码。
代码如下:
#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define endl "\n"
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int N=2e5+10;
int t,n,k;
int a[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--)
{
cin>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
int s=1;
int cnt=0;
for(int i=1;i<=n;i++)
{
if(a[i]==s)
{
cnt++;
s++;
}
}
cout<<(n-cnt+k-1)/k<<endl;
}
return 0;
}
C 题目链接:https://codeforces.com/contest/1768/problem/C

input:
3
1
1
5
5 3 4 2 5
2
1 1
output:
YES
1
1
YES
1 3 4 2 5
5 2 3 1 4
NO
题意:
给一个长度为 n 的数组 a,试构造排列 p 和 q ,使得对于任意 1<= i <= n ,满足 max{pi,qi} == ai,如果不能构造,输出NO,否则输出YES,并输出 p 和 q。
思路:
首先讨论是否有解。
无解的情况有两种:
数组 a 中,1出现2次及以上 ,或其他某个数出现3次及以上
构造后,发现 p 和 q 某个位置不满足要求(构造方式下面细说)
如何构造呢,直接暴力构造即可。
首先对数组 a 中所有第一次出现的数,直接构造到 p 对应的位置上。
数组 a 中尚未构造的数,构造到 q 对应的位置上。
最后,对于 p 中尚未构造的位置,我们按照其对应的 q上的数从小到大的顺序,填入最小的可以填入的数(没有在p中出现过)。q 的构造方式同理。
构造后判断是否满足要求,输出即可。
代码如下(写的太长了,很不美观,大家理解一下思想即可):
#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define endl "\n"
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int N=2e5+10;
int t,n;
int a[N];
int p[N],q[N];
bool vis[N],vis0[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--)
{
cin>>n;
map<int,int> mp,mp1,mp0;
for(int i=1;i<=n;i++)
{
vis[i]=0;
vis0[i]=1;
p[i]=0;
q[i]=0;
}
for(int i=1;i<=n;i++)
{
cin>>a[i];
mp1[a[i]]++;
if(!vis[a[i]])
{
p[i]=a[i];
vis[a[i]]=1;
mp[a[i]]=i;
}
else
{
q[i]=a[i];
vis0[a[i]]=0;
mp0[a[i]]=i;
}
}
bool flag=1;
for(auto x:mp1)
{
if(x.first==1)
{
if(x.second>=2)
{
flag=0;
}
}
else
{
if(x.second>2)
{
flag=0;
}
}
}
int k=1;
for(int i=1;i<=n;i++)
{
if(mp0[i])
{
if(p[mp0[i]]==0)
{
while(vis[k]) k++;
p[mp0[i]]=k;
k++;
}
}
}
k=1;
for(int i=1;i<=n;i++)
{
if(mp[i])
{
if(q[mp[i]]==0)
{
while(!vis0[k]) k++;
q[mp[i]]=k;
k++;
}
}
}
for(int i=1;i<=n;i++)
{
if(max(p[i],q[i])!=a[i])
{
flag=0;
break;
}
}
if(!flag)
{
cout<<"NO"<<endl;
continue;
}
else
{
cout<<"YES"<<endl;
}
for(int i=1;i<=n;i++)
{
cout<<p[i]<<" ";
}
cout<<endl;
for(int i=1;i<=n;i++)
{
cout<<q[i]<<" ";
}
cout<<endl;
}
return 0;
}
D 题目链接:https://codeforces.com/contest/1768/problem/D

input:
4
2
2 1
2
1 2
4
3 4 1 2
4
2 4 3 1
output:
0
1
3
1
题意:
给一个长度为 n 的排列 p ,给定一种操作,可以交换两个数 。问最少需要多少次操作,使得 p 恰好有一个逆序对。
思路:
首先提出一个结论:排列 p 恰好有一个逆序对的情形,当且仅当排列 p 两个相邻的位置,元素相反,其他位置下标与元素均相等。
思路比较简单,首先,原排列 p 中,元素与下标相等的位置不用动,其他位置需要移动才能有序。随后,这些需要移动的位置,其元素与对应下表会形成若干个环。对于一个长度为 k 的环,我们将其变为有序(也就是让所有元素回到对应的位置)需要的操作次数为 k - 1次。最后,我们考虑环内时候有两个位置相邻,如果存在至少一种相邻的情况,我们可以在该环操作的最后,不去操作这两个相邻的位置,最后得到一个相反的相邻对,满足题目要求,该环的操作次数为 k - 2 次。如果不存在环内有相邻位置的情况,那么我们将所有环归位后,多花一次操作将任意两个相邻位置交换即可。
环我们用并查集处理,两个元素 find 返回值相等,即在同一个环里。
结论:有相邻位置,代价为所有环的大小-1的和-1。无相邻位置,代价为所有环的大小-1的和+1。
代码如下:
#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define endl "\n"
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int N=2e5+10;
int t,n;
int fa[N],cnt[N];
int find(int x)
{
if(x==fa[x])
return x;
return fa[x]=find(fa[x]);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--)
{
cin>>n;
int a;
for(int i=1;i<=n;i++)
{
fa[i]=i;
cnt[i]=1;
}
for(int i=1;i<=n;i++)
{
cin>>a;
int p=find(a),q=find(i);
if(p!=q)
{
fa[p]=q;
cnt[q]+=cnt[p];
}
}
bool f=0;
for(int i=1;i<n;i++)
{
if(find(i)==find(i+1))
{
f=1;
break;
}
}
int sum=0;
for(int i=1;i<=n;i++)
{
if(fa[i]==i&&cnt[i]>1)
{
sum+=cnt[i]-1;
}
}
if(f)
{
cout<<sum-1<<endl;
}
else
{
cout<<sum+1<<endl;
}
}
return 0;
}