2018hdu杭电多校第四场 hdu6333 Problem B. Harvest of Apples (莫队 + 组合数 + 逆元)

Problem B. Harvest of Apples

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 1595    Accepted Submission(s): 603

Problem Description

There are n apples on a tree, numbered from 1 to n.
Count the number of ways to pick at most m apples.

Input

The first line of the input contains an integer T (1≤T≤10^5) denoting the number of test cases.
Each test case consists of one line with two integers n,m (1≤m≤n≤10^5).

Output

For each test case, print an integer representing the number of ways modulo 10^{9}+7.

Sample Input

2

5 2

1000 500

Sample Output

16

924129523

Source

2018 Multi-University Training Contest 4


题意:t组数据,每组给出n和m,求C_{n}^{0} +C_{n}^{1}+C_{n}^{2}...+C_{n}^{m}S_{n}^{m}

题解:

组合数求模要用到逆元,\frac{a}{b} \ mod \ p = \frac{a}{b} *1\ mod \ p=\frac{a}{b}*c\ mod \ p = a*c\ mod \ p 此时称 c 为 b 的逆元

假设c是b的逆元 b*c = 1\ \( mod \ p) ①,根据费马小定理:gcd(a, p) = 1 时,a^{p-1} \ mod \ p = 1 ② (p表示素数)

联立①②可得:b*c = c^{p-1}\mod p\Rightarrow b = c^{p-2}  所以模(p-2)就好

然后回到这题,显而易见可以得到{\color{Red} S_{n}^{m}=S_{n}^{m-1}+C_{n}^{m}}

根据组合数的递推公式C_{n}^{m} = C_{n-1}^{m} + C_{n-1}^{m-1}

S_{n}^{m} = C_{n}^{0} +C_{n}^{1}+C_{n}^{2}...+C_{n}^{m}

S_{n}^{m} = C_{n-1}^{0} +(C_{n-1}^{1} +C_{n-1}^{0})+(C_{n-1}^{2} + C_{n-1}^{1})...+(C_{n-1}^{m}+C_{n-1}^{m-1})

S_{n}^{m}=2 * (C_{n-1}^{0} +C_{n-1}^{1}+C_{n-1}^{2}...+C_{n-1}^{m-1})+C _{n}^{m}

{\color{Red} S_{n}^{m}=2 * S_{n-1}^{m}-C _{n-1}^{m}}

把上式的S_{n}^{m}变成S_{n}^{m-1}或者S_{n}^{m+1} 得到:

{\color{Red} S_{n-1}^{m}=(S_{n}^{m}+C _{n-1}^{m}) / 2}

{\color{Red} S_{n+1}^{m}=2*S_{n}^{m}-C _{n}^{m}}

根据红字的公式用莫队离线操作

代码:

#include 
#define mem(a, x) memset(a, x, sizeof(a))
#define rep(i,a,n) for (int i=a;i=a;i--)
using namespace std;
typedef long long ll;
const ll mod=1000000007;
ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
//------------------------------------head------------------------------------
const int N = 1e5+10;
ll fac[N], inv[N]; // fac 阶乘, inv 逆元 
ll inv2, val, ans[N]; // inv2 除2的逆元,val 中间答案,ans 答案 
int pos[N], t; // pos 分块 

void init() {
    // 除2的逆元 
    inv2 = powmod(2, mod-2); 
    
    // 预处理阶乘 
    fac[0] = fac[1] = 1;
    rep (i, 2, N) fac[i] = i * fac[i-1] % mod;
    
    // 求阶乘逆元 
    inv[N-1] = powmod(fac[N-1], mod-2);
	per (i, 0, N-1) inv[i] = inv[i+1] * (i+1) % mod;
}

ll comb(int n, int m) { // c(n, m) = n!/(m! * (n-m)!)
	return fac[n] * inv[m] % mod * inv[n-m] % mod;
}

struct node {
	int l, r, id;
} Q[N];

bool cmp(node a, node b) {
	if (pos[a.l] == pos[b.l]) return a.r < b.r;
	return a.l < b.l;
}

void addL(int n, int m) { // S(n,m) = 2 * S(n-1,m) - C(n-1,m)
	val = (2 * val % mod - comb(n-1, m) + mod) % mod;
}

void delL(int n, int m) { // S(n-1,m) = (S(n,m) + C(n-1,m)) / 2 
	val = (val + comb(n-1, m)) % mod * inv2 % mod;
}

void addR(int n, int m) { // S(n,m) = S(n,m-1) + C(n,m)
	val = (val + comb(n, m)) % mod;
}

void delR(int n, int m) { // S(n,m-1) = S(n,m) - C(n, m)
	val = (val - comb(n,m) + mod) % mod;
}

int main() {
	init();
	scanf("%d", &t);
	int sz = sqrt(N);
	rep (i, 1, t+1) {
		scanf("%d%d", &Q[i].l, &Q[i].r);
		pos[i] = i / sz;
		Q[i].id = i;
	}
	sort(Q+1, Q+t+1, cmp);
	int l = 1, r = 1;
	val = 2; // c(1,0) + c(1,1)
	rep (i, 1, t+1) {
		while (l < Q[i].l) addL(++l, r);
		while (l > Q[i].l) delL(l--, r);
		while (r < Q[i].r) addR(l, ++r);
		while (r > Q[i].r) delR(l, r--);
		ans[Q[i].id] = val;
	}
	rep (i, 1, t+1) printf("%lld\n", ans[i]);
}

 

你可能感兴趣的:(hdu,2018多校,题解)