HDU - 5976  简单逆元

首先预处理,分解为n位数至少要多大,然后upper_bound大概就知道拆分为几个数了.
然后考虑最优解,就是每位数劲量的大,因为是相乘,所以中间做多有一个空,我们记录中间空的位置和其实位置以及最后的位置就知道了答案.
前面的数的阶乘以及逆元可以预处理,阶乘的逆元也可以预处理.复制度在O(logn)

#include
#include
#include
#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;
}

你可能感兴趣的:(acm水题)