题意:已知P是一个排列,P[i]是得到第i位置的数字,例如:n=3,那么P={1,3,2},P[2]=3,P[3]=2。
接着给你一个函数f:代入一个i,j,当j>1时,将i所对应的位置的值P[i]代入,同时j-1,直到j==1,返回p[i]的值。
例如:一个排列P={2,3,4,5,1},f(2,4)=f(3,3)=f(4,2)=f(5,1)=5。
接着定义一个函数g:对于每一个i,g(i)的值是f(i,j)=i的最小一个j。现在给你n,a,b,让你构造一个排列P,对于每一个i,使g(i)==a或b。
思路:我们一点一点来,首先函数f很简单,其实就是一个位置转移的过程,当j==1时返回当前位置的里的值。我们重点看函数g,也就是看f(i,j)=i,这个表达式的意义我们自己找找规律其实就很好理解了。
我们可以这样想:函数f的转移,其实是图中节点的转移,当前节点是i,那么下一个呢?就是P[i],接下来呢?重复刚刚的操作即可,这样就很好理解了吧。
我们接着来看函数g:f(i,j)=i,我把函数f的意义和图的节点遍历联系起来,那么这个角度看待当前的函数g是不是就很简单了:就是以i为起点,经过不断的转移又回到i,那么转移次数就是j的大小啊,所以j最小,就是让我们构造一个环,j就是这个环的最小正周期。
现在函数g的意义已经明确了,那么推构造的规律:1的环1->1 ,2的环2->1 3的环2->3->1 4的环2->3->4->1,构造方式很明显了,将1-n的升序排列整体向左平移一次,第一个位置放到最后位置去即可。注意,环不一定有一个,比如样例1,这里不再推了,自己看样例应该已经可以理解了,由于我们的排列元素不能重复,所以会产生无解的情况,此时就是环的个数无法正确的得到。
关于环的个数,利用扩展欧几里得即可解出ax+by=n这个不定方程的一组非负整数解,就可以构造了,x,y所对应的值即周期为a的环x个,周期为b的环y个,且利用的元素不能重复,只能解释成这样了,剩下看代码吧,代码真的不长。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,a,b,tmp,x,y,flag;
int main()
{
while(~scanf("%lld%lld%lld",&n,&a,&b))
{
flag=0;
for(ll i=0;i<=1000000;i++) //暴力枚举x,y求不定方程的非负整数解,扩展欧几里得当然也行
{
tmp=n-i*a;
if(tmp<0)break;
else if(tmp%b==0)
{
flag=1;
x=i;y=tmp/b;
break;
}
}
if(flag==0){printf("-1\n");continue;} //没有非负整数解
tmp=1;
for(ll i=1;i<=x;i++) //构造x
{
for(ll j=tmp+1;j<tmp+a;j++)printf("%lld ",j);
printf("%lld ",tmp);
tmp+=a;
}
for(ll i=1;i<=y;i++) //构造y
{
for(ll j=tmp+1;j<tmp+b;j++)printf("%lld ",j);
printf("%lld ",tmp);
tmp+=b;
}
printf("\n");
}
return 0;
}