既不会莫队,也不会求组合数,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
#include
#include
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{
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.rreturn 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{
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{
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++
好好努力!