JZOI 4305. 【NOIP2015模拟11.3晚】七十和十七

洛谷AC通道!

设$f[i]$为长度为$i$的序列(不是排列)中计数器的大小。假设现在已知$f[i-1]$

那么第$i$位与前面的相对大小关系一共有i种

1.第$i$位相对大小为i,对计数器没有任何影响,直接转移 $f[i]=f[i-1]$

2.第$i$位相对大小小于$i$ (即$1~i-1$),设为$j$,那么计数器的值就需要增加$2^{j-1}$ (可以拿几个数试一下),对于前$i-1$个数,他们的顺序有$(i-1)!$种,所以$ f[i]=f[i-1]+2^{j-1}*(i-1)!$

综上,

$f[i]=f[i-1]+(f[i-1]+2^{1-1}*(i-1)!)+(f[i-1]+2^{2-1}*(i-1)!)+(f[i-1]+2^{3-1}*(i-1)!)...+(f[i-1]+2^{i-1-1}*(i-1)!)$

发现$2$的多少次方是一个等比数列,可以直接用前$i-1$项和公式求出来,剩下$i$个$f[i-1]$相加

最终得到$f[i]=f[i-1]*i+(2^{i-1}-1)*(i-1)!$

答案即为$f[n]/n!$

#include <bits/stdc++.h>
using namespace std;
#define N 100010
#define ll long long 
const int mod = (int)1e9 + 7;

inline int read(){
    int x = 0, s = 1;
    char c = getchar();
    while(!isdigit(c)){
        if(c == '-') s = -1;
        c = getchar();
    }
    while(isdigit(c)){
        x = x * 10 + (c ^ '0');
        c = getchar();
    }
    return x * s;
} 

ll qpow(ll a, ll b){
    ll sum = 1;
    while(b){
        if(b & 1) sum = (sum * a) % mod;
        a = (a * a) % mod;
        b >>= 1;
    }
    return sum;
}

ll f[N];
ll n;


int main(){
//    freopen("seventeen10.in", "r", stdin);
//    freopen("seventeen10.out", "w", stdout);
    n = read();
    ll num = 1, tot = 1;
    for(ll i = 1; i <= n; i++){
        f[i] = (f[i - 1] * i % mod + (tot - 1) * num % mod) % mod;
        tot = (tot << 1) % mod;
        num = (num * i) % mod;
    } 
    cout << f[n] * qpow(num, mod - 2) % mod << endl;
    return 0;
}

部分转载于大佬的博客

原文地址:https://www.cnblogs.com/wondering-world/p/13396446.html