http://codeforces.com/gym/103102
I. Modulo Permutations
题意:给定 n n n ,问满足 p i mod p i + 1 ≤ 2 p_i \text{ mod } p_{i+1} \le 2 p i mod p i + 1 ≤ 2 ,(p n + 1 = p 1 p_{n+1}=p_1 p n + 1 = p 1 ),的排列 p p p 的个数。
dp
首先 1 和 2 可以放在任意位置。
由于这个题目要的其实是个环,所以可以从 1 的位置切开,变成 1,x,x,x,2,x,x,x,x。1 和 2 把整个排列分成两段。每一段内一定是递减的,因为如果有递增,则 p i mod p i + 1 = p i > 2 p_i \text{ mod } p_{i+1}=p_{i}>2 p i mod p i + 1 = p i > 2 。
从小到大遍历,每一轮在上一轮的基础上插入数 i i i ,考虑把 i i i 放在第一段还是第二段。
假设现在枚举到 i i i ,数组 d p [ j ] dp[j] d p [ j ] 表示与 i i i 处于不同段的数中最大的数为 j j j 时的方案数。s u m [ j ] sum[j] s u m [ j ] 表示 j j j 的所有因数的 d p dp d p 值之和,即 ∑ d ∣ j d p [ d ] \sum\limits_{d|j}dp[d] d ∣ j ∑ d p [ d ] 。
有两种情况:
把 i i i 和 i − 1 i-1 i − 1 放在同一段,此时 d p [ j ] , j ≠ i − 1 dp[j],j\neq i-1 d p [ j ] , j = i − 1 ,都不需要更新,因为是否插入了 i i i 对方案数没有影响。
i i i 和 i − 1 i-1 i − 1 不在同一段,此时更新的是 d p [ i − 1 ] dp[i-1] d p [ i − 1 ] 。i i i 显然在它所在段的第一位,它后面的数 x x x 必须要是 i , i − 1 , i − 2 i,i-1,i-2 i , i − 1 , i − 2 的因数才能满足 i mod x ≤ 2 i\text{ mod }x\le 2 i mod x ≤ 2 。因此 d p [ i − 1 ] = ∑ d ∣ i d p [ d ] + ∑ d ∣ i − 1 d p [ d ] + ∑ d ∣ i − 2 d p [ d ] = s u m [ i ] + s u m [ i − 1 ] + s u m [ i − 2 ] dp[i-1]=\sum\limits_{d|i}dp[d]+\sum\limits_{d|i-1}dp[d]+\sum\limits_{d|i-2}dp[d]=sum[i]+sum[i-1]+sum[i-2] d p [ i − 1 ] = d ∣ i ∑ d p [ d ] + d ∣ i − 1 ∑ d p [ d ] + d ∣ i − 2 ∑ d p [ d ] = s u m [ i ] + s u m [ i − 1 ] + s u m [ i − 2 ] 。
统计答案时遍历 i ∈ [ 3 , n − 1 ] i\in[3,n-1] i ∈ [ 3 , n − 1 ] 求和。由于一段可能为空,所以还需要 +2。
由于题目需要的是环,所以还要 *n。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include <bits/stdc++.h> #define debug(x) cout << #x << ":\t" << (x) << endl; using namespace std;#define ll long long #define ull unsigned ll const int N = 2e6 + 10 ;const int INF = 0x3f3f3f3f ;const ll inf = 0x3f3f3f3f3f3f3f3f ;const ll mod = 1e9 + 7 ;const ll inv2 = (mod + 1 ) / 2 ;typedef pair<int , int > pii;typedef pair<int , ll> pil;int n;ll dp[N], sum[N]; int main () { scanf ("%d" , &n); if (n == 1 ) { puts ("1" ); return 0 ; } if (n == 2 ) { puts ("2" ); return 0 ; } dp[3 ] = 2 ; for (int i = 1 , j; (j = i * 3 ) <= n; i++) { sum[j] = (sum[j] + dp[3 ]) % mod; } for (int i = 4 ; i <= n; i++) { dp[i - 1 ] = (sum[i] + sum[i - 1 ] + sum[i - 2 ]) % mod; for (int j = 1 , k; (k = j * (i - 1 )) <= n; j++) { sum[k] = (sum[k] + dp[i - 1 ]) % mod; } } ll ans = 2 ; for (int i = 3 ; i < n; i++)ans = (ans + dp[i]) % mod; printf ("%lld\n" , ans * n % mod); return 0 ; }