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
1
1
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
1
题意:给一个长度为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;
}