2019 Multi-University Training Contest 5 - 1007 permutation 2

题目链接

题意:
给你N个数,范围是 [1 , N], 数与数之间两两不相同,现在要你将这些数排序,使得第一个数为x,第N个数为y,且相邻两个数的差 |aj - ai| <= 2,输出总共有多少种情况。

比赛时队友写了个暴力,找到其中一些规律,对于N,x,y,其前x+1个数,后N-y+2个数总是保持不变的,比如20,4,16,那么所有情况肯定是4,2,1,3,5,…,15,17,19,20,18,16.在变的只是中间数而已。
特判:当x=1或者y=N时,保持不变的只有第一个数或最后一个数。

画图可知:
从x出发到y,必先向前走,每次走两格留一条路给自己回来,所以4,2固定,如果走4,3就没有路回来了,回去到y也必经过1,3,5,同理,反推y,要到达y,必定是从N到达y,每次也都是两格两格走,所以从N到y的路径是20,18,16,然后15,17,19也同理固定,因此所有情况肯定是4,2,1,3,5,…,15,17,19,20,18,16.
所以可转化为x+1到y-1有多少种,其中x+1,y-1固定,即5到15.

从5开始走,当你走到13,14,15时,须保证前面的路都走完了,因为接下来必须去走15,17,19,20,18,16,而16是真正意义上的终点,到达16后不可继续往14走,所以我们在走的时候,只能一步一步走,或者向后走两步,往前退一步,再往后走两步,即是f(i-1) + f(i-2+1-2).如果走两步后不回头,那么就会断掉自己的路。

所以f(i) = f(i-1) + f(i-3).我们可通过预处理把前1e5个数求出来,然后直接输出f(k).而k = 15 - 5 + 1.假设l = x + 1, r = y - 1, 则 k = r - l + 1.

AC_Code:

#include 
using namespace std;

#define ll long long
#define mod 998244353
const int maxn = 1e5+5;
int t,n,x,y,l,r;
ll a[maxn] = {0,1,1};

void init(){
    for(int i=3; i<=1e5; ++i){
        a[i] = a[i-1] + a[i-3];
        a[i] %= mod;
    }
}

inline void solve(){
    ll sum = 0;
    l = x == 1 ? 1 : x + 1;
    r = y == n ? n : y - 1;
    cout << a[r-l+1] << endl;
}

int main(){
    init();
    cout << a[10];
    while(cin >> t){
        while(t--){
            cin >> n >> x >> y;
            solve();
        }
    }
    return 0;
}

你可能感兴趣的:(2019 Multi-University Training Contest 5 - 1007 permutation 2)