题解 AT987 高桥君

题意

$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 <cmath>
#include <algorithm>
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;
} //ask(n,m)询问C_n^m mod 10^9+7
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;
} //1为从(n,k)到(n,k+1),2为到(n,k-1),3为到(n+1,k),4为到(n-1,k)
}

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 高桥君


猜你喜欢

转载自www.cnblogs.com/peterchan1/p/11641167.html
今日推荐