hdu6333 Harvest of Apples(莫队+求组合数)

既不会莫队,也不会求组合数,GG。

莫队讲解:http://www.cnblogs.com/CsOH/p/5904430.html#4034690

逆元求组合数:https://blog.csdn.net/arrowlll/article/details/52629448

1.为什么用莫队算法?

题目最坏情况有10W (次查询) * 10W ( 每次查询 (10W,10W) ),这样绝对超时。

推公式:

我们定义S(n,m) = C(n,0) + C(n,1) + .... + C(n,m-1) + C(n,m)

则S(n,m-1) = S(n,m) - C(n,m);

S(n,m+1) = S(n,m) + C(n,m+1);

S(n+1,m) = 2*S(n,m) - C(n,m);

S(n-1,m) = ( S(n,m)+C(n-1,m) ) / 2

我们推出了公式,而且题目是没要求在线,是符合莫队算法的,查了很多资料,复杂度是(n+t)*log(n),t是查询次数,可以接受。

2.怎么解决求组合数?

递推公式还需要求出组合数,如果传统做法本地跑C(100000,50000)都不过。

这题的数据是10万,开几个10万的数组是没问题的,所以我们考虑 用逆元求组合数,因为在模固定的情况下求逆元,是可以用递推的方式来求逆元的,复杂度O(n),而且题目是多组查询,这种有记忆性的求组合数法是非常有益的,所以  求组合数的问题 迎刃而解。

同学用了Lucas但是超时,我也没学过,后来看了看,这个算法是没有记忆的,我个人觉得吧,①如果是mod不是质数(只有a和b互质,a才有关于b的逆元,这样不能保证所有数都关于mod有逆元),②或者n非常大,不能用数组记忆的时候,逆元求组合数就行不通了,这些情况用Lucas就比较稳了。

//推出公式,发现可以用莫队算法,
//将每个查询排序
#include<stdio.h>
#include<math.h>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn = 1e5+10;
const int len = (int)sqrt(100000);
const LL mod = 1e9+7;

LL fac[maxn]={1,1},inv[maxn]={1,1},f[maxn]={1,1};
LL cell(LL a,LL b)
{
      return fac[a]*inv[b]%mod*inv[a-b]%mod;
}
void init()
{
    for(int i=2;i<maxn;i++)
    {
        fac[i]=fac[i-1]*i%mod;
        f[i]=(mod-mod/i)*f[mod%i]%mod;
        inv[i]=inv[i-1]*f[i]%mod;
    }
}
struct Q
{
    LL l,r,id;
    LL S;
}q[maxn];

int buf[maxn];
LL ans = 0;
int cmp1(Q q1,Q q2)
{
    if(buf[q1.l]==buf[q2.l]) return q1.r<q2.r;
    return q1.l < q2.l;
}
int cmp2(Q q1,Q q2)
{
    return q1.id < q2.id;
}
int main()//Lucas
{
    int t;
    scanf("%d",&t);
    init();
    for(int i=1;i<=100000;i++)
    {
        buf[i] = i/len+1;
    }
    for(int i=1;i<=t;i++)
    {
        scanf("%d%d",&q[i].l,&q[i].r);
        q[i].id = i;
        q[i].S = 0ll;
    }
    sort(q+1,q+1+t,cmp1);
    LL l = 0;
    LL r = 0;
    ans = 1ll;
    
    for(int i=1;i<=t;i++)
    {
        while(l<q[i].l)
        {
            ans = ((2ll*ans)%mod - cell(l,r)+mod)%mod;
            l++;    
        }
        while(l>q[i].l)
        {
            //要用逆元啊
            ans = ((ans + cell(l-1,r))%mod*inv[2])%mod;
            l--;    
        }
        while(r>q[i].r)
        {
            ans = (ans - cell(l,r)+mod)%mod;
            r--;    
        }
        while(r<q[i].r)
        {
            ans = (ans + cell(l,r+1))%mod;
            r++;    
        }
        q[i].S = (ans+mod)%mod;
    }
    sort(q+1,q+1+t,cmp2);
    
    for(int i=1;i<=t;i++)
    {
        printf("%lld\n",q[i].S);
    }
    return 0;
}//546ms G++

好好努力!

猜你喜欢

转载自blog.csdn.net/zark721/article/details/81391911