这道题一开始以为是lucas,然后就tle,赛后知道是莫队算法的时候还愣住了,刚才参考了别人的思路才知道这个状态可以推出关系的,通过左右状态的转变,离线化分块操作到达极低的时间复杂度,不仅对莫队算法认识加深了,也是复习了一下逆元,思路在下图中给出,代码就是普通的莫队
另外附上别人总结的莫队适用场合,认真思考以实现问题转化
①题目必须离线
②能够以极少的时间推出旁边区间(一般是O(1))
③没有修改或者修改不太苛刻
④基于分块,分块不行,它也好不了哪里去(何况现在还有可持久化数据结构维护的分块)
但莫队的思想美妙,代码优美,你值得拥有。莫队的排序思想也为众多离线处理的题目提供了完整的思路。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<cmath>
using namespace std;
#define maxn 100005
#define p 1000000007
long long fac[maxn];
long long inv[maxn];//逆元的阶乘
long long inv1[maxn];//逆元
long long ans[maxn];
struct node
{
int l, r;
int id;
int block;
}a[maxn];
bool cmp(node a, node b)
{
return a.block == b.block ? a.r < b.r : a.l < b.l;
}
void init()
{
fac[0] = fac[1] = 1;
inv1[0] = inv1[1] = 1;
inv[0] = inv[1] = 1;
for(int i = 2; i < maxn; i ++)
{
fac[i] = i * fac[i-1] % p;
inv1[i] = (p-p/i)*inv1[p%i]%p;
inv[i] = inv[i-1]*inv1[i]%p;
}
}
long long c(int r, int l)
{
return r < l ? 0 : fac[r]*inv[l]%p*inv[r-l]%p;
}
int main()
{
int unit = sqrt(maxn);
init();
int n;
int l = 1, r = 1;
scanf("%d",&n);
for(int i = 1; i <= n; i ++)
{
scanf("%d%d",&a[i].r,&a[i].l);
a[i].id = i;
a[i].block = a[i].l/unit;
}
sort(a + 1, a + 1 + n, cmp);
long long ans1 = 2;
for(int i = 1; i <= n; i ++)
{
while(l < a[i].l){ans1=(ans1+c(r,l+1))%p;l++;}
while(l > a[i].l){ans1=(ans1-c(r,l)+p)%p;l--;}
while(r > a[i].r){ans1=(ans1+c(r-1,l))*inv1[2]%p;r--;}
while(r < a[i].r){ans1=(2*ans1-c(r,l)+p)%p;r++;}
ans[a[i].id] = ans1;
}
for(int i = 1; i <= n; i ++)
printf("%lld\n",ans[i]);
return 0;
}