HDU - 5976  簡單逆元

首先預處理,分解爲n位數至少要多大,然後upper_bound大概就知道拆分爲幾個數了.
然後考慮最優解,就是每位數勁量的大,因爲是相乘,所以中間做多有一個空,我們記錄中間空的位置和其實位置以及最後的位置就知道了答案.
前面的數的階乘以及逆元可以預處理,階乘的逆元也可以預處理.複製度在O(logn)

#include<cstdio>
#include<iostream>
#include<algorithm>
#define ll long long
const int maxn = 5e4;

const int mod = 1e9+7;
using namespace std;

int sum2[maxn+10];
ll fac[maxn+10];
ll inv[maxn+10];

void init(){
    sum2[0] = sum2[1] = 0;
    fac[0]=fac[1]=1;
    for(int i = 2; i < maxn; i++){
        fac[i]= fac[i-1]*i%mod;
        sum2[i] = sum2[i-1] + i;
    }
    inv[1] = 1;
    for(int i = 2; i < maxn; i++){
        inv[i]=(mod-mod/i)*inv[mod%i]%mod;
    }
}

void e_gcd(int a, int b, ll &d, ll &x, ll &y){
    if(!b){
        d = a; x = 1; y = 0;
        return ;
    }
    e_gcd(b, a%b, d, y, x);
    y -= x*(a/b);
}
int main(){
    init();
    int t;scanf("%d",&t);
    while(t--){
        int n;scanf("%d",&n);
        if(n==1){
            printf("1\n");
            continue;
        }
        //拆分位數
        int it = upper_bound(sum2, sum2+maxn, n) - sum2 -2;
        n-=sum2[it+1];
        //提升位數
        int stx, stlen, edlen, edy, temp;
        stx = 2 + n/it;
        edy = 1+it + (n/it) + (n%it!=0);
        edlen = n%it;
        if(edlen)temp = edy - edlen;
        else temp = 0;
        ll x,y,d;
        e_gcd(fac[stx-1],mod, d, x, y);
        if(edlen){
            printf("%lld\n",((fac[edy]*inv[temp]%mod+mod)%mod*x%mod+mod)%mod);
        }else{
            printf("%lld\n",(fac[edy]*x%mod+mod)%mod);
        }
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章