题意
求长度为(n)且满足以下条件的排列有多少:
- (p_i mod p_{i+1} le 2),其中(p_{n+1}=p_1)。
题解
可以将排列分为两个段,一段以1为结尾,一段以2为结尾。这两段序列必须满足递减且满足条件。
那么相当于求,需要将(n)~(3)递减的数依次分别填入这两个段中合法的方案有多少。可以用当前两个段的结尾作为状态。设((x,y))代表1段的结尾为(x),2段的结尾为(y)时的方案数。假设(n)~(x)都已经放完了,现在在放(x-1)(显然有(i> x)),那么有转移方程:
[egin{aligned}
(x,i) & o (x-1,i) & x-1放在1段\
& o (x-1,x) & x-1放在2段,且(x-1 mod i) le 2
end{aligned}
]
直接可以压掉(x)那一维。第一个转移相当于将数组复制一遍,第二个转移只用转移((x-1 mod i) le 2)的(i)到(x)位置。用二维的模拟一下就知道了。最后时间复杂度为(O(nlog n)),调和级数。
细节:
使用(n+1)代表什么都不放时结尾的状态,转移的时候不要漏了(n+1)。
最后计算到((1,2)),答案为(ncdot (1,2))
#include <bits/stdc++.h>
#define endl '
'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define mp make_pair
#define seteps(N) fixed << setprecision(N)
typedef long long ll;
using namespace std;
/*-----------------------------------------------------------------*/
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
#define INF 0x3f3f3f3f
const int N = 1e6 + 10;
const int M = 1e9 + 7;
const double eps = 1e-5;
ll dp[N];
int main() {
IOS;
int n;
cin >> n;
if(n <= 2) {
cout << n << endl;
return 0;
}
dp[n + 1] = 1;
for(int i = n; i >= 2; i--) {
ll tot = dp[n + 1];
if(i > 3) {
for(int j = (i - 1); j <= n; j += i - 1) {
if(j > i && j <= n) tot += dp[j];
if(j + 1 > i && j + 1 <= n) tot += dp[j + 1];
if(j + 2 <= n) tot += dp[j + 2];
tot %= M;
}
} else {
for(int j = i + 1; j <= n; j++) {
tot += dp[j];
tot %= M;
}
}
dp[i] = (dp[i] + tot) % M;
}
cout << dp[2] * n % M << endl;
}