Codeforces Round #309 (Div. 2) D - Kyoya and Permutation ,和dp相关的xjb搞的题

来源:互联网 发布:如何跟淘宝达人合作 编辑:程序博客网 时间:2024/06/14 12:53

这种题 看起来题意都会很复杂的:
对于 1-n的序列 :比如4.1.6.2.5.3 。。。。 1对应4,4对应2,所以(142)是一个集合,同理有(36)(5)
现在要将这些集合排序,集合内部按从大到小排列,集合与集合之间按第一个数从小到大排。比如:
上述的序列 变成:(421)(5)(63)

现在定义一些序列,经过上述的操作之后不会发生改变,比如:1 3 2 4 ; (1)(32)(4)
现在给出n,k。
求1-n的序列中 所有不会操作之后不会改变的序列, 并且按字典序排的第k个
比如样例:
n=4 有
1 2 3 4
1 2 4 3
1 3 2 4
2 1 3 4
2 1 4 3
第三个就是1 3 2 4
我刚刚开始看到这道题,想到的就是 前几天做的经典dp,栅栏高低起伏的那道题, 这道题应该也是一个一个数字的确定过去
但是很难想明白 以1 开头的序列到底有多少个

但其实可以写一写,枚举一下n的序列:

>
n=1———n=2———–n=3———–n=4—– - - - - - -n=5
1————1 2 ———–1 2 3———-1 2 3 4———–1 2 3 4 5
———— -2 1 ———–2 1 3——- -2 1 3 4———–2 1 3 4 5
…—————————1 3 2———1 3 2 4———–1 3 2 4 5
.——————————————–1 2 4 3———–1 2 4 3 5
.——————————————–2 1 4 3———–2 1 4 3 5
.————————————————————–1 2 3 5 4
.————————————————————–2 1 3 5 4
.————————————————————–1 3 2 5 4

仔细看看其实不难得出规律吧:
每一位,如果想要造作之后不变,那么他永远都只能和他前一位的数字是出于同一个集合,否则都不能满足不改变这个条件!
那么就分成了两个子问题:
1.如果i和i-1 进行交换,那么dp[i]+=dp[i-2]
2.i 不和 i-1 交换 dp[i]+=dp[i-1]
也许你无法证明,但是仔细看看写的这些东西,感觉还挺有道理的。于是就可以得到每一个i ,yes(和前一位交换)的个数,以及no(不和前一位交换)的个数,并能用递归预处理求出1-50 的所有dp

struct node{    __int64 yes;    __int64 no;    __int64 sum;}dp[60];dp[1].yes=dp[1].no=dp[1].sum=1;    dp[2].yes=dp[2].no=1;dp[2].sum=2;    dp[3].yes=1;    for(int i=3;i<=50;i++){        dp[i].no=dp[i-1].no+dp[i-2].no;    }    for(int i=4;i<=50;i++){         dp[i].yes=dp[i-1].yes+dp[i-2].yes;        dp[i].sum=dp[i].yes+dp[i].no;    }```想到这一步我们就可以开始一步一步的逐个确认了,仔细看看n=5,是 21 交换的个数 =54交换的个数 =dp[5].yescnt初识为2,若是k<no,那么 21 不会交换,则ans[cnt-1]=cnt-1,让pos--,cnt++ 继续判断下一个否则 ,21 交换,则ans[cnt-1]=cnt,ans[cnt]=cnt-1 ; 因为交换,所以可以确定两个位置pos-2,cnt+=2k-=dp[pos].no;然后我就开始xjb写了,写了大半天还好是写出来了:ac代码
#include<iostream>#include<stdio.h>#include<string.h>#include<math.h>#include<algorithm>#include<stdlib.h>#include<queue>#include<stack>#include<map>#include<vector>#define mem(a) memset(a,0,sizeof(a))#define ll __int64#define INF 0x7fffffff   //INT_MAX#define inf 0x3f3f3f3f   //const double PI = acos(-1.0);const double e = exp(1.0);template<class T> T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }template<class T> T lcm(T a, T b) { return a / gcd(a, b) * b; }using namespace std;#define mod 1000000007const int maxn=2000001;struct node{    __int64 yes;    __int64 no;    __int64 sum;}dp[60];int ans[55];int main(){    freopen("1.txt","r",stdin);    int n;    __int64 k;    dp[1].yes=dp[1].no=dp[1].sum=1;    dp[2].yes=dp[2].no=1;dp[2].sum=2;    dp[3].yes=1;    for(int i=3;i<=50;i++){        dp[i].no=dp[i-1].no+dp[i-2].no;    }    for(int i=4;i<=50;i++){        dp[i].yes=dp[i-1].yes+dp[i-2].yes;        dp[i].sum=dp[i].yes+dp[i].no;    }//    printf("%I64d %I64d  \n",dp[46].yes,dp[47].yes);    while(~scanf("%d %I64d",&n,&k)){          int pos=n;          int cnt=2;          mem(ans);          while(cnt<=n ){//            printf("pos=%d\n",pos);             if(dp[pos].no>=k){ //从低位找 不换,再找交换的                 pos--;                 ans[cnt-1]=cnt-1;                 cnt++;             }             else if(dp[pos].no<k){//                printf("yes   %d\n",cnt);                 k-=dp[pos].no;                 pos-=2;                 ans[cnt-1]=cnt;                 ans[cnt]=cnt-1;                 cnt+=2;             }          }          for(int i=1;i<=n;i++){             if(!ans[i]) printf("%d ",i);             else printf("%d ",ans[i]);          }          cout<<endl;    }    return 0;}

“`

0 0