题意
$T$个询问,每次询问给出$n,k$,你需要回答
数据范围:$1le Tle 100000, 0le n,kle 100000$
莫队
这题需要用普通莫队算法。
我们将每个询问$(n,k)$离线存下来,并用莫队处理区间问题的方法将其分块后排序。($n$当做左端点,$k$当做右端点)
我们知道要想用莫队算法,就需要知道$(n,k)$的答案和$(n,k+1),(n,k-1),(n-1,k),(n+1,k)$这四种询问的答案的联系。也就是说当前指针指向$(n,k)$时,我们需要$O(1)$地算出上述询问的答案。
组合数
先找$(n,k)$的答案和$(n,k+1),(n,k-1)$的答案的关系。
很容易就能发现:
然后我们来找$(n,k)$和$(n-1,k),(n+1,k)$的关系,注意到有这个等式:
所以我们可以这么推:
然后反过来推出
综上所述:
注意到我们在转移时需要以$O(1)$的复杂度求出单个组合数,因为
所以预处理1-100000中每个数的逆元,以及阶乘的逆元,就能做到。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
|
#include #include using namespace std;
#define MOD 1000000007ll #define MAXN 200005 typedef long long ll; typedef unsigned long long ull; #define min(a, b) (((a) < (b)) ? (a) : (b)) #define max(a, b) (((a) > (b)) ? (a) : (b))
ll () { char c = getchar(); ll ret = 0; while (c < '0' || c > '9') c = getchar(); while (c >= '0' && c <= '9') ret = ret * 10 + c - '0', c = getchar(); return ret; }
int N; ll blocklen, pn = 1, pk = 0, anst = 1, inv[MAXN], mul[MAXN], mulinv[MAXN], ans[MAXN], inv2 = (MOD + 1) >> 1; struct query { ll n, k, id, block; } Q[MAXN]; bool cmp(query q1, query q2) { if (q1.block != q2.block) return q1.block < q2.block; else return q1.k < q2.k; } ll ask(ll n, ll m) { return ((mul[n] * mulinv[m]) % MOD * mulinv[n - m]) % MOD; } void move(int t) { switch(t) { case 1: anst = ((anst + ask(pn, pk + 1)) % MOD + MOD) % MOD, ++pk; break; case 2: anst = ((anst - ask(pn, pk)) % MOD + MOD) % MOD, --pk; break; case 3: anst = (((2 * anst) % MOD - ask(pn, pk)) % MOD + MOD) % MOD, ++pn; break; case 4: anst = ((inv2 * ((anst + ask(pn - 1, pk)) % MOD)) % MOD + MOD) % MOD, --pn; break; } }
int main() { N = read(), blocklen = sqrt(N); int i; inv[1] = 1, mul[0] = mulinv[0] = 1; for (i = 2; i <= 100000; ++i) inv[i] = ((MOD - MOD / i) * inv[MOD % i]) % MOD, mul[i] = (mul[i - 1] * (ll)i) % MOD; for (i = 1; i <= 100000; ++i) mul[i] = (mul[i - 1] * i) % MOD, mulinv[i] = (mulinv[i - 1] * inv[i]) % MOD; for (i = 1; i <= N; ++i) Q[i].n = read(), Q[i].k = read(), Q[i].id = i, Q[i].block = Q[i].n / blocklen; sort(Q + 1, Q + N + 1, cmp); for (i = 1; i <= N; ++i) { while (pn < Q[i].n) move(3); while (pn > Q[i].n) move(4); while (pk < Q[i].k) move(1); while (pk > Q[i].k) move(2); ans[Q[i].id] = anst; } for (i = 1; i <= N; ++i) printf("%lldn", ans[i]); return 0; }
|
原文:大专栏 题解 AT987 高桥君