洛谷传送门
BZOJ传送门
题目描述
求有多少种长度为 n n 的序列 ,满足以下条件:
1∼n 1 ∼ n 这 n 个数在序列中各出现了一次
若第 i i 个数 的值为 i i ,则称 是稳定的。序列恰好有 m m 个数是稳定的
满足条件的序列可能很多,序列数对 取模。
输入输出格式
输入格式:
第一行一个数 T T ,表示有 组数据。
接下来 T T 行,每行两个整数 、 m m 。
输出格式:
输出 行,每行一个数,表示求出的序列数
输入输出样例
输入样例#1:
5
1 0
1 1
5 2
100 50
10000 5000
输出样例#1:
0
1
20
578028887
60695423
说明
测试点 1 ~ 3: T=1000 T = 1000 , n≤8 n ≤ 8 , m≤8 m ≤ 8 ;
测试点 4 ~ 6: T=1000 T = 1000 , n≤12 n ≤ 12 , m≤12 m ≤ 12 ;
测试点 7 ~ 9: T=1000 T = 1000 , n≤100 n ≤ 100 , m≤100 m ≤ 100 ;
测试点 10 ~ 12: T=1000 T = 1000 , n≤1000 n ≤ 1000 , m≤1000 m ≤ 1000 ;
测试点 13 ~ 14: T=500000 T = 500000 , n≤1000 n ≤ 1000 , m≤1000 m ≤ 1000 ;
测试点 15 ~ 20: T=500000 T = 500000 , n≤1000000 n ≤ 1000000 , m≤1000000 m ≤ 1000000 。
解题分析
前置知识 乘法逆元
首先我们有 inv[1]=1 i n v [ 1 ] = 1
设 N=i×k+p N = i × k + p
放到 mod N m o d N 意义下就有 i×k+p=0 i × k + p = 0
同乘 inv[i] i n v [ i ] , inv[p] i n v [ p ] 可得
k× inv[p]+inv[i]=0 k × i n v [ p ] + i n v [ i ] = 0
可得 inv[i]=−k× inv[p] i n v [ i ] = − k × i n v [ p ]
inv[i]=−⌊N/i⌋×inv[N mod i] i n v [ i ] = − ⌊ N / i ⌋ × i n v [ N m o d i ]
我们就有了 O(N) O ( N ) 线筛逆元的方法了。
下面看这道题:
首先一眼看出我们可以钦定
m
m
个点为稳定点, 所以答案先乘一个。
对于剩下的 n−m n − m 个点,设有 k k 个点时方案数为,我们考虑从当前有 k−1 k − 1 位向 k k 位递推:
首先我们肯定要将另一位和第 k k 位上的数(我们默认为k)互换一下, 这样换有种方法。
然后对于每种方法我们就有 val[k−1] v a l [ k − 1 ] 种构造方案。(前 k−1 k − 1 位排列即可)
这样就完了吗?
显然不是的。 在 val[k−1] v a l [ k − 1 ] 中我们只考虑了第 i i 位不能为的情况, 但我们换入了一个不会重复的值 k k ,导致第位上可以放 k k 。(感觉讲的好迷啊QAQ)
考虑放在第 p p 位上的情况。其实就等同于前位排列,且不会存在上述情况。
所以我们得到递推公式:
边界条件位 val[0]=1 v a l [ 0 ] = 1 , val[1]=0 v a l [ 1 ] = 0 , val[2]=1 v a l [ 2 ] = 1 。
然后我们就可以筛出逆元、阶乘、阶乘逆元以及 val[] v a l [ ] , O(1) O ( 1 ) 回答每个询问。
代码如下:
#include <cstdio>
#include <cctype>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define ll long long
#define MOD 1000000007
#define MX 1000050
template <class T>
IN void in(T &x)
{
x = 0; R char c = gc;
W (!isdigit(c)) c = gc;
W (isdigit(c))
x = (x << 1) + (x << 3) + c - 48, c = gc;
}
int T, all, cot;
ll fac[MX], val[MX], inv[MX], finv[MX], ans;
void pre()
{
fac[1] = inv[1] = finv[1] = finv[0] = 1;
for (R int i = 2; i <= 1000000; ++i) fac[i] = i * fac[i - 1] % MOD;
for (R int i = 2; i <= 1000000; ++i) inv[i] = -(MOD / i) * inv[MOD % i], inv[i] = (inv[i] % MOD + MOD) % MOD;
for (R int i = 2; i <= 1000000; ++i) finv[i] = finv[i - 1] * inv[i] % MOD;
val[1] = 0; val[2] = val[0] = 1;
for (R int i = 3; i <= 1000000; ++i) val[i] = (i - 1) * ((val[i - 1] + val[i - 2]) % MOD) % MOD;
}
IN ll C()
{
ll ret = fac[all] * finv[cot] % MOD;
ret = ret * finv[all - cot] % MOD;
return ret;
}
int main(void)
{
in(T); pre();
W (T--)
{
in(all), in(cot);
ans = C() * val[all - cot] % MOD;
printf("%lld\n", ans);
}
}