Gym - 103102I(dp)

题目大意:

长度为 n ( ≤ 1 0 6 ) n(\le 10^6) n(106)的全排列,有多少种排列,满足 ∀ i ∈ [ 1 , n ] ,   p i % p i + 1 ≤ 2 ,   ( p n + 1 = p 1 ) \forall i \in [1,n],\,p_i\%p_{i+1}\le2,\,(p_{n+1}=p_1) i[1,n],pi%pi+12,(pn+1=p1)

解题思路:

  • 首先可以发现有两个特别的数:1,2,不管这两个数两侧是什么数都符合上面的条件,而且上面的条件: p i % p i + 1 ≤ 2 p_i\%p_{i+1}\le2 pi%pi+12还包含了一个条件: p i > p i + 1 p_i > p_{i+1} pi>pi+1

  • 那么题目就有一个等价的问题:序列 { n , n − 1 , n − 2 , . . . , 3 } \{n,n-1,n-2,...,3\} {n,n1,n2,...,3}有多少种子序列的选法,使得选择的子序列以及未选择的子序列都满足相邻取模小于等于2

  • 那么每个数有两种放法,然后最后还要 O ( n ) O(n) O(n)遍历检查是否合法,复杂度为 O ( 2 n ⋅ n ) O(2^n\cdot n) O(2nn),这样的复杂度显然是不能接受的

  • 其实发现当前的放法仅仅只跟两个序列结尾的数有关,那么 d p dp dp方程为:
    先 设 f [ i ] [ j ] 为 第 一 个 序 列 结 尾 为 i , 另 一 个 为 j 当 前 n ∼ x 都 已 经 放 在 两 个 序 列 中 , 且 合 法 , 那 么 一 定 存 在 一 个 序 列 结 尾 为 x , 设 另 一 个 为 i ( i > x ) 当 前 放 的 是 x − 1 , 所 以 有 f [ x − 1 ] [ i ] + = f [ x ] [ i ] 如 果 ( i % ( x − 1 ) ) ≤ 2 , 则 有 f [ x ] [ x − 1 ] + = f [ x ] [ i ] ↔ f [ x − 1 ] [ x ] + = f [ x ] [ i ] , ( i % ( x − 1 ) ≤ 2 ) \\先设f[i][j]为第一个序列结尾为i,另一个为j 当前n\sim x都已经放在两个序列中,且合法,那么一定存在一个序列结尾为x,设另一个为i(i>x) \\ 当前放的是x-1,所以有f[x-1][i]+=f[x][i] \\ 如果(i\%(x-1))\le2,则有f[x][x-1]+=f[x][i] \leftrightarrow f[x-1][x]+=f[x][i],(i\%(x-1)\le2) \\ f[i][j]ijnxxi(i>x)x1f[x1][i]+=f[x][i](i%(x1))2f[x][x1]+=f[x][i]f[x1][x]+=f[x][i],(i%(x1)2)

  • 对于这个复杂度也是 O ( n 2 ) O(n^2) O(n2),虽然比之前优秀,但仍然不能接受,我们可以把这里的转移看作如下的表:

x x x x + 1 x+1 x+1 x + 2 x+2 x+2
x x x f [ x ] [ x + 1 ] f[x][x+1] f[x][x+1] f [ x ] [ x + 2 ] f[x][x+2] f[x][x+2]
x − 1 x-1 x1 f [ x − 1 ] [ x ] = ∑ i % ( x − 1 ) ≤ 2 f [ x ] [ i ] f[x-1][x]=\sum_{i\%(x-1)\le2}f[x][i] f[x1][x]=i%(x1)2f[x][i] f [ x − 1 ] [ x + 1 ] = f [ x ] [ x + 1 ] f[x-1][x+1]=f[x][x+1] f[x1][x+1]=f[x][x+1] f [ x − 1 ] [ x + 2 ] = f [ x ] [ x + 2 ] f[x-1][x+2]=f[x][x+2] f[x1][x+2]=f[x][x+2]
  • 所以第一维实际上是可以不要的,而 i % ( x − 1 ) ≤ 2 i\%(x-1)\le2 i%(x1)2 i i i可以通过筛法直接得到,最终时间复杂度是 O ( n l n ( n ) ) O(nln(n)) O(nln(n))
  • 而我们最终计算出的 d p [ 4 ] , d p [ 5 ] , . . . , d p [ n ] dp[4],dp[5],...,dp[n] dp[4],dp[5],...,dp[n]其实就相当于 f [ 3 ] [ 4 ] , f [ 3 ] [ 5 ] , . . . , f [ 3 ] [ n ] f[3][4],f[3][5],...,f[3][n] f[3][4],f[3][5],...,f[3][n]
  • 所以最终答案就是 ( d p [ 4 ] + . . . + d p [ n ] + 1 ) ∗ 2 (dp[4]+...+dp[n]+1)*2 (dp[4]+...+dp[n]+1)2,加1是因为,还有可能某个序列不放,乘2是因为1不一定要在第一个序列中,它可以和2互换

AC代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
vector<int> d[N];
int dp[N];
void add(int &x, int y) {
    x += y; 
    if (x >= mod) x -= mod;
    else if (x < 0) x += mod;
}
int n;
int main() {
    cin >> n;
    if (n <= 2) {
        cout << n << endl;
        return 0;
    }
    dp[n] = 1;
    for (int i = n - 1; i >= 4; i--) {
        dp[i] = 1;
        int x = i - 1;
        for (int k = x; k <= n; k += x) {
            for (int j = 0; j <= 2; j++)
                if (k + j > i) add(dp[i], dp[k + j]);
        }
    }
    int ans = 1;
    for (int i = 4; i <= n; i++) add(ans, dp[i]);
    cout << 1ll * ans * 2 * n % mod << endl;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值