传送门 ----------------链接
题意:求C(n,0)+C(n,1)+C(n,2)+....C(n,m)
注意到题目的n和m都很大,并且有1e5组数据,暴力一定会超时。一开始想的是C(n,0)+C(n,1)+C(n,2)+....C(n,m)能否化简为
一个组合数表达式,在化简过程中发现了如下规律。
于是可以将每次询问看作区间,可以利用莫队算法离线处理出所有的询问。
需要注意的是第四个的除2,直接除答案是不对的,应该乘上2的(mod-2)次方
Code:
#include
using namespace std;
#define maxn 100005
#define LL long long
const LL mod = 1000000007;
int block_size, T;
struct node
{
int L, R, id;
bool operator <(const node &b) const
{
return (L / block_size == b.L / block_size) ? (R < b.R) : (L / block_size < b.L / block_size);
}
} query[maxn];
LL ans, inv2;
LL res[maxn], inv[maxn], factorial[maxn];
LL quickPow(LL a, LL b)
{
LL temp = 1;
while(b)
{
if(b & 1)
temp = temp * a % mod;
a = a * a % mod;
b >>= 1;
}
return temp;
}
void Init()
{
factorial[0] = 1;
for(int i = 1; i <= maxn; i++)
factorial[i] = factorial[i - 1] * i % mod;
inv[maxn] = quickPow(factorial[maxn], mod - 2);
for(int i = maxn - 1; i >= 0; i--)
inv[i] = inv[i + 1] * (i + 1) % mod;
inv2 = inv[2];
}
LL comb(int n, int m)
{
return factorial[n] * inv[m] % mod * inv[n - m] % mod;
}
inline LL Nplus(int l, int r)
{
return ans=(2 * ans % mod - comb(r - 1, l) % mod + mod) % mod;
}
inline LL Mplus(int l, int r)
{
return ans=(ans % mod + comb(r, l) % mod) % mod;
}
inline LL Mdele(int l, int r)
{
return ans=(ans % mod - comb(r, l + 1) % mod + mod) % mod;
}
inline LL Ndele(int l, int r)
{
return ans=(ans % mod + comb(r, l) % mod) * inv2 % mod;
}
void solve()
{
int l = 1, r = 1, i;
ans = 2;
for(i = 0; i < T; i++)
{
while(r < query[i].R) Nplus(l, ++r);
while(l < query[i].L) Mplus(++l, r);
while(l > query[i].L) Mdele(--l, r);
while(r > query[i].R) Ndele(l, --r);
res[query[i].id] = ans;
}
}
int main()
{
int i;
Init();
memset(query, 0, sizeof(query));
scanf("%d", &T);
for(i = 0; i < T; i++)
{
scanf("%d%d", &query[i].R, &query[i].L);
block_size = max(block_size, query[i].R);
query[i].id = i;
}
block_size = (int) sqrt(block_size * 1.0);
sort(query, query + T);
solve();
for(i = 0; i < T; i++)
printf("%I64d\n", res[i]);
return 0;
}