【NOIP模拟赛】 permutation 数学(打表)

biubiu~~~

这道题卡读题卡得很死......首先他告诉我们读循环的时候要顺着圈读,然后又说这个圈在数列上要以最大数开始读,而且以这样的循环的首数排序,得到的序列与原序列一样那么他就是可行序列,所以我们发现任何一个可行序列他的循环一定是连续的一块,因为假设我们有一个循环 x->y->z->a->x,并且假设x最大,那么在数列上只会出现x,y,z,a,那么我们根据循环的定义,他们的原本位置也在这几个里面那么也就是各个循环是一块一块的,然后我们在看刚才的假设,我们发现z是最大的!!!然后发现当且仅当一个循环里是两个数他才是合法的,然后我们归纳一下,一个可行排列里的循环只能是两个相邻的数交换位置,然后我们把这个数列往后排发现是Fibonacci数列,然后就循环一波就好了。

这道题当然如果你是在是没推出来的话也可以用看脸的打表找规律,但是我还真觉得如果看懂题的话不如推..

#include <cstdio>
typedef long long LL;
namespace Fib{
  LL f[100];
  inline void get(){
    f[0]=f[1]=f[2]=1;
    for(int i=3;i<=50;i++)
      f[i]=f[i-1]+f[i-2];
  }
}
namespace Main{
  int n;
  LL k;
  int Ans[55];
  inline void get(){
    scanf("%d%lld",&n,&k);
    int pos=1;
    while(pos<=n){
      LL temp=0;
      int now=0;
      for(int i=0;i<n;i++){
        temp+=Fib::f[i];
        if(temp>=k){
          now=i;
          break;
        }
      }
      if(temp==k){
        if(now==0){
          for(int i=pos;i<=n;i++){
            Ans[i]=i;
          }
          break;
        }
        for(int i=pos;i<=n-now-1;i++)
          Ans[i]=i;
        for(int i=n-now;i+1<=n;i+=2)
          Ans[i+1]=i,Ans[i]=i+1;
        if(Ans[n]==0)Ans[n]=n;
        break;
      }
      k-=temp-Fib::f[now];
      for(int i=pos;i<=n-now-1;i++)
        Ans[i]=i;
      Ans[n-now]=n-now+1;
      Ans[n-now+1]=n-now;
      pos=n-now+2;
    }
  }
}
int main(){
  Fib::get();
  Main::get();
  for(int i=1;i<=Main::n;i++)
    printf("%d ",Main::Ans[i]);
  return 0;
}
原文地址:https://www.cnblogs.com/TSHugh/p/7327599.html