acwing 309装饰围栏 大致思路 (预览)

原题链接


这题挺锻炼思维的qwq

首先题目中序列的大小比较方式是和字符串的大小比较方式一样的, 序列中第一个数较小的序列的排名总是小于序列中第一个数较大的序列的排名, 故算出(F[i][j])表示, 第一个数为(j)的规模为(i)的序列的个数, 对其做个前缀和, 就可以确定满足要求的序列的第一个数, 然后就继续查第二个数是多少……

但是上面只是大致的思路, 实际上要想求出(F[i][j])还得考虑第一个数是波峰还是波谷。

但是上面还是只是大致的思路, 实际上查出的第一个数是([1,n])中排名为某个数的数, 第二个数是([1,n])去掉第一个数后排名为某个数的数。

但是还没完, 在查第二个数是也要注意第二个数是波峰开头还是波谷开头。


AC代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 25;
#define li long long

li f[maxn][maxn][2];
li S[maxn][maxn], Sf[2][maxn][maxn];
bool vis[maxn];

int main()
{
    f[2][1][0] = f[2][2][1] = 1ll;
    f[3][1][0] = f[3][2][0] = f[3][2][1] = f[3][3][1] = 1ll;
    for(int i=4;i<=20;++i) {
        for(int j=1; j<=i; ++j) {
            for(int k=1; k<j; ++k) f[i][j][1] += f[i-1][k][0];
            for(int k=j; k<=i-1; ++k) f[i][j][0] += f[i-1][k][1];
        }
    }
    f[1][1][1] = f[1][1][0] = 1ll;
    int k;
    li n,c;
    cin >> k; while(k--) {
        memset(vis, 0, sizeof vis);
        scanf("%lld%lld", &n,&c);
        
        li las, p;
        for(int i=1; i<=n; ++i) {
            if(c<=f[n][i][1]) {
                las = i, p = 1;
                break;
            }
            c -= f[n][i][1];
            if(c<=f[n][i][0]) {
                las = i, p = 0;
                break;
            }
            c -= f[n][i][0];
        }
        vis[las] = true;
        cout << las;
        for(int i=2; i<=n; ++i) {
            p ^= 1;
            int rk = 0;
            //寻找第i位
            for(int j=1; j<=n; ++j) if(!vis[j]) {
                ++rk;
                if((p==1&&j>las) || (p==0&&j<las)) {
                    if(f[n-i+1][rk][p] >= c)
                    {
                        las = j;
                        vis[las] = true;
                        cout << ' ' << j;
                        break;
                    }
                    c -= f[n-i+1][rk][p];
                }
            }
        }
        putchar('
');
    }
    return 0;
}
原文地址:https://www.cnblogs.com/tztqwq/p/12749934.html