HDU 5976 逆元

1、HDU 5976 Detachment  

2、题意:给一个正整数x,把x拆分成多个正整数的和,这些数不能有重复,要使这些数的积尽可能的大,输出积。

3、总结:首先我们要把数拆得尽可能小,这样积才会更大(当然不能拆1)。所以容易想到是拆成2+3+...+n+s=x,先求出n即2+3+...+n<x<2+3+...+n+(n+1),然后将某个数向右平移s个单位变为n+1即可。注意:(1)预处理出前缀和,前缀积。(2)将某个数移到n+1,要除这个数再乘n+1,这里要用逆元,也要预处理出来。

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define F(i,a,b)  for (int i=a;i<b;i++)
#define FF(i,a,b) for (int i=a;i<=b;i++)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
typedef long long ll;
const int N = 1e5+10;
const ll mod = 1e9+7;

int sum[N],inv[N];
ll mul[N];
//int tot;
void init()
{
    sum[1]=0;
    mul[1]=inv[1]=1;
    F(i,2,N) {
        sum[i]=i+sum[i-1];     //前缀和
        mul[i]=i*mul[i-1]%mod;  //前缀积
        inv[i]=(mod-mod/i)*inv[mod%i]%mod;  //O(n)时间内求逆元,要求mod是素数
        //tot=i;
    }
}

int main()
{
    int T,x;
    scanf("%d", &T);
    init();
    while(T--)
    {
        scanf("%d",&x);
        if(x<5) printf("%d\n", x);
        else
        {
            /* int l=lower_bound(sum+1,sum+1+tot,x)-sum;    //可以直接函数二分,找出第一个大于或等于x的位置
            if(sum[l]==x) { printf("%d\n", mul[l]); continue; }   
            l--;     */

            int l=2,r=N,mid=(l+r)>>1;    //特注:二分写法,mid先定义,不要在while里面
            while(l+1<r) {
                //mid=(l+r)>>1;     //注:一开始这样写,错得莫名其妙,还是养成习惯,先在上面定义mid
                sum[mid]>x ? r=mid : l=mid;
                mid=(l+r)>>1;
            } 
            ll ans;
            int k,num;
            num=x-sum[l], k=l+1-num;
            if(2+num>l) ans=mul[l]*inv[2]%mod*(2+num)%mod;
            else  ans=mul[l]*inv[k]%mod*(l+1)%mod;
            printf("%lld\n", (ans+mod)%mod);
        }
    }

    return 0;
}
View Code
posted @ 2016-11-13 21:09  v9fly  阅读(333)  评论(0编辑  收藏  举报