[Codeforces] combinatorics (R1600) Part.6
题单:https://codeforces.com/problemset?tags=combinatorics,1201-1600
1326C. Permutation Partitions
原题指路:https://codeforces.com/problemset/problem/1326/C
题意
给定一个 1 ∼ n 1\sim n 1∼n的排列 p 1 , ⋯ , p n p_1,\cdots,p_n p1,⋯,pn,将其划分为不相交的 k k k段区间,使得每一段的最大值之和最大,并求取得最大值的划分方案数,后者答案对 998244353 998244353 998244353取模.设第 i ( 1 ≤ i ≤ k ) i\ \ (1\leq i\leq k) i (1≤i≤k)段区间为 [ l i , r i ] [l_i,r_i] [li,ri],则最大化 ∑ i = 1 k max l i ≤ j ≤ r i p j \displaystyle \sum_{i=1}^k \max_{l_i\leq j\leq r_i} p_j i=1∑kli≤j≤rimaxpj.
第一行输入两个整数 n , k ( 1 ≤ k ≤ n ≤ 2 e 5 ) n,k\ \ (1\leq k\leq n\leq 2\mathrm{e}5) n,k (1≤k≤n≤2e5).第二行输入一个 1 ∼ n 1\sim n 1∼n的排列 p 1 , ⋯ , p n p_1,\cdots,p_n p1,⋯,pn.
思路
为使得 ∑ i = 1 k max l i ≤ j ≤ r i p j \displaystyle \sum_{i=1}^k \max_{l_i\leq j\leq r_i} p_j i=1∑kli≤j≤rimaxpj最大,显然应取 1 ∼ n 1\sim n 1∼n中的前 k k k大的数,即将它们分在不同的 k k k个区间.记录前 k k k大的数的下标 p o s [ ] pos[] pos[],则相邻两个前 k k k大的数所在的区间的分割线可取中间任一位置,将 p o s [ ] pos[] pos[]升序排列后相邻两位置的下标差之积即为方案数.
代码
const int MOD = 998244353;
void solve() {
int n, k; cin >> n >> k;
vii a(n);
for (int i = 0; i < n; i++) {
int x; cin >> x;
a[i] = { x,i };
}
sort(rall(a));
vi pos(k); // 前k大的数的下标
ll ans1 = 0;
for (int i = 0; i < k; i++) {
ans1 = ans1 + a[i].first;
pos[i] = a[i].second;
}
sort(all(pos));
int ans2 = 1;
for (int i = 1; i < k; i++) ans2 = (ll)ans2 * (pos[i] - pos[i - 1]) % MOD;
cout << ans1 << ' ' << ans2;
}
int main() {
solve();
}
1328B. K-th Beautiful String
原题指路:https://codeforces.com/problemset/problem/1328/B
题意
将所有包含 ( n − 2 ) (n-2) (n−2)个’a’和 2 2 2个’b’的字符串按字典序升序排列,求排名第 k k k的字符串.
有 t ( 1 ≤ t ≤ 1 e 4 ) t\ \ (1\leq t\leq 1\mathrm{e}4) t (1≤t≤1e4)组测试数据.每组测试数据输入两个整数 n , k ( 3 ≤ n ≤ 1 e 5 , 1 ≤ k ≤ min { 2 e 9 , n ( n − 1 ) 2 } ) n,k\ \ \left(3\leq n\leq 1\mathrm{e}5,1\leq k\leq \min\left\{2\mathrm{e}9,\dfrac{n(n-1)}{2}\right\}\right) n,k (3≤n≤1e5,1≤k≤min{2e9,2n(n−1)}).数据保证所有测试数据的 n n n之和不超过 1 e 5 1\mathrm{e}5 1e5.
思路
显然只需确定两个’b’所在的位置.先确定第一个’b’的位置,枚举下标 i ∈ [ n − 2 , 0 ] i\in[n-2,0] i∈[n−2,0],若 k ≤ n − i − 1 k\leq n-i-1 k≤n−i−1,则当前位置是第一个’b’的位置,进而第二个’b’的下标为 ( n − k ) (n-k) (n−k);否则令 k − = n − i − 1 k-=n-i-1 k−=n−i−1,即跳过所有当前位置为’b’的字符串.
代码
void solve() {
int n, k; cin >> n >> k;
string ans(n, 'a');
for (int i = n - 2; i >= 0; k -= (n - i - 1), i--) { // 枚举第一个'b'的位置
if (k <= (n - i - 1)) {
ans[i] = ans[n - k] = 'b';
cout << ans << endl;
return;
}
}
}
int main() {
CaseT // 单测时注释掉该行
solve();
}
1391C. Cyclic Permutations
原题指路:https://codeforces.com/problemset/problem/1391/C
题意
给定一个 1 ∼ n 1\sim n 1∼n的排列 p p p,现按如下方式建立一张包含 n n n个节点的无向图:①对 ∀ i ∈ [ 1 , n ] \forall i\in[1,n] ∀i∈[1,n],找到最大的 j s . t . 1 ≤ j < i j\ s.t.\ 1\leq j<i j s.t. 1≤j<i且 p j > p i p_j>p_i pj>pi,添加一条从节点 i i i到节点 j j j的无向边;②对 ∀ i ∈ [ 1 , n ] \forall i\in[1,n] ∀i∈[1,n],找到最小的 j s . t . i < j ≤ n j\ s.t.\ i<j\leq n j s.t. i<j≤n且 p j > p i p_j>p_i pj>pi,添加一条从节点 i i i到节点 j j j的无向边.称一个排列 p p p是循环排列,如果它对应的图中至少存在一个简单环.给定整数 n ( 3 ≤ n ≤ 1 e 6 ) n\ \ (3\leq n\leq 1\mathrm{e}6) n (3≤n≤1e6),求 1 ∼ n 1\sim n 1∼n的排列中循环排列的个数,答案对 1 e 9 + 7 1\mathrm{e}9+7 1e9+7取模.
思路
排列 p p p对应的图至少存在一个简单环当且仅当 ∃ i < j < k s . t . a i > a j , a k > a j \exists i<j<k\ s.t.\ a_i>a_j,a_k>a_j ∃i<j<k s.t. ai>aj,ak>aj.显然若 p p p对应的图不存在环,则 p p p是一个单峰排列,则 a n s = n ! − c n t ans=n!-cnt ans=n!−cnt,其中 c n t cnt cnt为单峰排列的个数.
设元素 n n n在下标 i i i的位置,将剩下的 ( n − 1 ) (n-1) (n−1)个元素分为两组,第一组有 ( i − 1 ) (i-1) (i−1)个元素,第二组有 ( n − i ) (n-i) (n−i)个元素,将第一组升序排列后放在下标 i i i的左边,将第二组降序排列后放在下标 i i i的右边,有 C n − 1 i − 1 C_{n-1}^{i-1} Cn−1i−1种方案.故 c n t = ∑ i = 1 n C n − 1 i − 1 = ∑ i = 0 n − 1 C n − 1 i = 2 n − 1 , a n s = n ! − 2 n − 1 \displaystyle cnt=\sum_{i=1}^n C_{n-1}^{i-1}=\sum_{i=0}^{n-1}C_{n-1}^i=2^{n-1},ans=n!-2^{n-1} cnt=i=1∑nCn−1i−1=i=0∑n−1Cn−1i=2n−1,ans=n!−2n−1.
c n t cnt cnt的另一求法:考虑构造一个单峰排列.将 n , ( n − 1 ) , ⋯ , 1 n,(n-1),\cdots,1 n,(n−1),⋯,1依次插入双端队列的队首或队尾,除元素 n n n外,其余的 ( n − 1 ) (n-1) (n−1)个元素可任意选择插入队首或队尾,有 c n t = 2 n − 1 cnt=2^{n-1} cnt=2n−1种方案.
代码
const int MOD = 1e9 + 7;
void solve() {
int n; cin >> n;
int ans = 1;
for (int i = 2; i <= n; i++) ans = (ll)ans * i % MOD;
cout << ((ans - qpow(2, n - 1, MOD)) % MOD + MOD) % MOD;
}
int main() {
solve();
}
1433E. Two Round Dances
原题指路:https://codeforces.com/problemset/problem/1433/E
题意
n ( 2 ≤ n ≤ 20 ) n\ \ (2\leq n\leq 20) n (2≤n≤20)( n n n为偶数)个人进行两轮跳舞,每轮跳舞共 n 2 \dfrac{n}{2} 2n人,每个人只能参加其中一轮,每轮跳舞的人有序.求方案数.
思路
先从 n n n个人中选出 n 2 \dfrac{n}{2} 2n个人在第一轮跳舞,共 C n n 2 C_n^\frac{n}{2} Cn2n种方案.在选出的第一组中固定第一个跳舞的人,剩下的 ( n 2 − 1 ) \left(\dfrac{n}{2}-1\right) (2n−1)个人任意排列,有 ( n 2 − 1 ) ! \left(\dfrac{n}{2}-1\right)! (2n−1)!种方案,同理第二组的方案也为该数.两组人数相等,故结果要除以 2 2 2,即 a n s = C n n 2 ⋅ ( n 2 − 1 ) ! ⋅ ( n 2 − 1 ) ! 2 = n ! n 2 2 ans=\dfrac{C_n^\frac{n}{2}\cdot \left(\dfrac{n}{2}-1\right)!\cdot \left(\dfrac{n}{2}-1\right)!}{2}=\dfrac{n!}{\dfrac{n^2}{2}} ans=2Cn2n⋅(2n−1)!⋅(2n−1)!=2n2n!.
20 ! = 2432902008176640000 20!=2432902008176640000 20!=2432902008176640000,故最终结果不会爆ll.
代码
void solve() {
int n; cin >> n;
ll ans = 1;
for (int i = 2; i <= n; i++) ans *= i;
cout << ans / (n * n / 2);
}
int main() {
solve();
}
1436C. Binary Search
原题指路:https://codeforces.com/problemset/problem/1436/C
题意
上述代码实现了在下标从 0 0 0开始的有序序列 a [ ] a[] a[]中查找 x x x.问 1 ∼ n 1\sim n 1∼n的所有排列中有多少个排列使得 x x x出现在下标 p o s pos pos(下标从 0 0 0开始)的位置,且上述代码能成功找到 x x x,答案对 1 e 9 + 7 1\mathrm{e}9+7 1e9+7取模.
第一行输入三个整数 n , x , p o s ( 1 ≤ x ≤ n ≤ 1000 , 0 ≤ p o s ≤ n − 1 ) n,x,pos\ \ (1\leq x\leq n\leq 1000,0\leq pos\leq n-1) n,x,pos (1≤x≤n≤1000,0≤pos≤n−1).
思路
在二分过程中,若 m i d < p o s mid<pos mid<pos,则 a m i d ≤ x a_{mid}\leq x amid≤x;若 m i d > p o s mid>pos mid>pos,则 a m i d > x a_{mid}>x amid>x.模拟二分过程,统计需要 < x <x <x的数的个数 c n t 1 cnt_1 cnt1和需要 > x >x >x的数的个数 c n t 2 cnt_2 cnt2,从 1 ∼ n 1\sim n 1∼n中的 s u m 1 sum_1 sum1个 < x <x <x的数中选 c n t 1 cnt_1 cnt1个任意排列, s u m 2 sum_2 sum2个 > x >x >x的数中选 c n t 2 cnt_2 cnt2个任意排列,其余 ( n − c n t 1 − c n t 2 − 1 ) (n-cnt_1-cnt_2-1) (n−cnt1−cnt2−1)个元素任意排列,其中 − 1 -1 −1是去掉了 x x x,故 a n s = A s u m 1 c n t 1 ⋅ A s u m 2 c n t 2 ⋅ ( n − c n t 1 − c n t 2 − 1 ) ! ans=A_{sum_1}^{cnt_1}\cdot A_{sum_2}^{cnt_2}\cdot (n-cnt_1-cnt_2-1)! ans=Asum1cnt1⋅Asum2cnt2⋅(n−cnt1−cnt2−1)!.
代码
const int MAXN = 1005;
const int MOD = 1e9 + 7;
int n, x, pos;
int fac[MAXN], ifac[MAXN];
void init() { // 预处理阶乘及其逆元
fac[0] = 1;
for (int i = 1; i < MAXN; i++) fac[i] = (ll)fac[i - 1] * i % MOD;
ifac[MAXN - 1] = qpow(fac[MAXN - 1], MOD - 2, MOD);
for (int i = MAXN - 1; i; i--) ifac[i - 1] = (ll)ifac[i] * i % MOD;
}
int A(int n, int m) { // 排列数A(n,m)
return (ll)fac[n] * ifac[n - m] % MOD;
}
void solve() {
init();
cin >> n >> x >> pos;
int cnt1 = 0, cnt2 = 0; // 需要<x、>x的数个数
int l = 0, r = n;
while (l < r) { // 模拟二分过程
int mid = l + r >> 1;
if (mid <= pos) l = mid + 1, cnt1 += (mid < pos);
else r = mid, cnt2++;
}
cout << (ll)A(x - 1, cnt1) * A(n - x, cnt2) % MOD * fac[n - cnt1 - cnt2 - 1] % MOD;
}
int main() {
solve();
}
1452D. Radio Towers
原题指路:https://codeforces.com/problemset/problem/1452/D
题意 ( 2 s 2\ \mathrm{s} 2 s)
x x x轴上有编号 0 ∼ ( n + 1 ) 0\sim (n+1) 0∼(n+1)的 ( n + 2 ) ( 1 ≤ n ≤ 2 e 5 ) (n+2)\ \ (1\leq n\leq 2\mathrm{e}5) (n+2) (1≤n≤2e5)个城市,依次在坐标 0 , ⋯ , ( n + 1 ) 0,\cdots,(n+1) 0,⋯,(n+1)的位置.坐标 1 , ⋯ , n 1,\cdots,n 1,⋯,n的位置上各自独立地有 1 2 \dfrac{1}{2} 21的概率造一个信号塔,每个信号塔有一个能量 p ∈ [ 1 , n ] p\in [1,n] p∈[1,n].若在坐标 i i i处有信号塔,则所有满足 ∣ c − i ∣ < p |c-i|<p ∣c−i∣<p的城市 c c c都会收到信号.现要造信号塔,使得 0 0 0号、 ( n + 1 ) (n+1) (n+1)号城市不能收到信号,且 1 , ⋯ , n 1,\cdots,n 1,⋯,n号城市都受到且只收到一个信号塔的信号,求满足要求的概率.可以证明答案可以写成分数的形式,输出该分数模 998244353 998244353 998244353的结果.
思路
每个信号塔可视为以其为中心的一个长度为奇数的区间,问题转化为用若干个长度为奇数的区间覆盖 [ 1 , n ] [1,n] [1,n]的概率.
d p [ i ] dp[i] dp[i]表示用若干个长度为奇数的区间覆盖区间 [ 1 , i ] [1,i] [1,i]的方案数.按坐标 i i i处是否造信号塔分类:①若造,则该塔只能取 p = 1 p=1 p=1,方案数即覆盖区间 [ 1 , i − 1 ] [1,i-1] [1,i−1]的方案数,即 d p [ i − 1 ] dp[i-1] dp[i−1];②若不造,为使得坐标 i i i的位置被覆盖,需将当前方案的最后一个区间的长度 + = 2 +=2 +=2,即 d p [ i − 2 ] dp[i-2] dp[i−2].状态转移方程: d p [ n ] = d p [ n − 1 ] + d p [ n − 2 ] dp[n]=dp[n-1]+dp[n-2] dp[n]=dp[n−1]+dp[n−2],即Fibonacci数列.
显然总情况数为 2 n 2^n 2n,故 a n s = f i b n 2 n ans=\dfrac{fib_n}{2^n} ans=2nfibn,其中 f i b n fib_n fibn表示Fibonacci数列的第 n n n项.
代码
const int MOD = 998244353;
void solve() {
int n; cin >> n;
vi fib(n + 1);
fib[1] = 1;
for (int i = 2; i <= n; i++) fib[i] = ((ll)fib[i - 1] + fib[i - 2]) % MOD;
cout << qpow(qpow(2, n, MOD), MOD - 2, MOD) * fib[n] % MOD;
}
int main() {
solve();
}
1462E1. Close Tuples (easy version)
原题指路:https://codeforces.com/problemset/problem/1462/E1
题意 ( 2 s 2\ \mathrm{s} 2 s)
给定一个长度为 n n n的序列 a 1 , ⋯ , a n a_1,\cdots,a_n a1,⋯,an,求其中使得 i < j < z i<j<z i<j<z且 max { a i , a j , a z } − min { a i , a j , a z } ≤ 2 \max\{a_i,a_j,a_z\}-\min\{a_i,a_j,a_z\}\leq 2 max{ai,aj,az}−min{ai,aj,az}≤2的 ( i , j , z ) (i,j,z) (i,j,z)三元组的个数.
有 t ( 1 ≤ t ≤ 2 e 5 ) t\ \ (1\leq t\leq 2\mathrm{e}5) t (1≤t≤2e5)组测试数据.每组测试数据第一行输入一个整数 n ( 1 ≤ n ≤ 2 e 5 ) n\ \ (1\leq n\leq 2\mathrm{e}5) n (1≤n≤2e5).第二行输入 n n n个整数 a 1 , ⋯ , a n ( 1 ≤ a i ≤ n ) a_1,\cdots,a_n\ \ (1\leq a_i\leq n) a1,⋯,an (1≤ai≤n).数据保证所有测试数据的 n n n之和不超过 2 e 5 2\mathrm{e}5 2e5.
思路
满足条件的有序三元组 ( a i , a j , a z ) (a_i,a_j,a_z) (ai,aj,az)有如下几类:① ( x , x + 2 , x + 2 ) (x,x+2,x+2) (x,x+2,x+2);② ( x , x + 1 , x + 2 ) (x,x+1,x+2) (x,x+1,x+2);③ ( x , x + 1 , x + 1 ) (x,x+1,x+1) (x,x+1,x+1);④ ( x , x , x + 2 ) (x,x,x+2) (x,x,x+2);⑤ ( x , x , x + 1 ) (x,x,x+1) (x,x,x+1);⑥ ( x , x , x ) (x,x,x) (x,x,x).统计每个数出现的次数,枚举三元组的最小元素 x x x,组合数即可.
代码
ll C(int n, int m) { // 组合数C(n,m)
if (n <= m) return n == m;
else {
ll res = 1;
for (int i = n - m + 1; i <= n; i++) res = res * i / (i - n + m);
return res;
}
}
void solve() {
int n; cin >> n;
vi cnt(n + 5); // 防止越界
for (int i = 0; i < n; i++) {
int a; cin >> a;
cnt[a]++;
}
ll ans = 0;
for (int i = 1; i <= n; i++) {
ans += (ll)cnt[i] * C(cnt[i + 2], 2);
ans += (ll)cnt[i] * cnt[i + 1] * cnt[i + 2];
ans += (ll)cnt[i] * C(cnt[i + 1], 2);
ans += C(cnt[i], 2) * cnt[i + 2];
ans += C(cnt[i], 2) * cnt[i + 1];
ans += C(cnt[i], 3);
}
cout << ans << endl;
}
int main() {
CaseT // 单测时注释掉该行
solve();
}
1475C. Ball in Berland
原题指路:https://codeforces.com/problemset/problem/1475/C
题意 ( 2 s 2\ \mathrm{s} 2 s)
有编号 1 ∼ a 1\sim a 1∼a的 a a a个男生和编号 1 ∼ b 1\sim b 1∼b的 b b b个女生,他们组成了 k k k对舞伴 ( x , y ) ( 1 ≤ x ≤ a , 1 ≤ y ≤ b ) (x,y)\ \ (1\leq x\leq a,1\leq y\leq b) (x,y) (1≤x≤a,1≤y≤b).从中选出两对舞伴,使得选中的 4 4 4个人不同,求方案数.
有 t ( 1 ≤ t ≤ 1 e 4 ) t\ \ (1\leq t\leq 1\mathrm{e}4) t (1≤t≤1e4)组测试数据.每组测试数据第一行输入三个整数 a , b , k ( 1 ≤ a , b , k ≤ 2 e 5 ) a,b,k\ \ (1\leq a,b,k\leq 2\mathrm{e}5) a,b,k (1≤a,b,k≤2e5).第二行输入 k k k个整数 a 1 , ⋯ , a k a_1,\cdots,a_k a1,⋯,ak,分别表示每对舞伴中男生的编号.第三行输入 k k k个整数 b 1 , ⋯ , b k b_1,\cdots,b_k b1,⋯,bk,分别表示每对舞伴中女生的编号.数据保证每组测试数据给定的 k k k对舞伴相异,且所有测试数据的 a , b , k a,b,k a,b,k之和不超过 2 e 5 2\mathrm{e}5 2e5.
思路
c n t a [ a i ] cnta[a_i] cnta[ai]表示 a i a_i ai号男生在舞伴中出现的次数, c n t b [ b i ] cntb[b_i] cntb[bi]表示 b i b_i bi号女生在舞伴中出现的次数.对第 i i i对舞伴,其对答案的贡献为总舞伴数 − a [ i ] -a[i] −a[i]的重复次数 − b [ i ] -b[i] −b[i]的重复次数 + 1 +1 +1,其中 + 1 +1 +1是因为 ( a [ i ] , b [ i ] ) (a[i],b[i]) (a[i],b[i])多减了一次.因每队舞伴都被多算了一次,故最后答案需除以 2 2 2.
代码
void solve() {
int n, m, k; cin >> n >> m >> k;
vi a(k), b(k);
vi cnta(n + 1); // cnta[a[i]]表示a[i]号男生在舞伴中出现的次数
vi cntb(m + 1); // cntb[b[i]]表示b[i]号男生在舞伴中出现的次数
for (int i = 0; i < k; i++) {
cin >> a[i];
cnta[a[i]]++;
}
for (int i = 0; i < k; i++) {
cin >> b[i];
cntb[b[i]]++;
}
ll ans = 0;
for (int i = 0; i < k; i++) ans += k - cnta[a[i]] - cntb[b[i]] + 1;
cout << ans / 2 << endl;
}
int main() {
CaseT // 单测时注释掉该行
solve();
}
1475E. Advertising Agency
原题指路:https://codeforces.com/problemset/problem/1475/E
题意 ( 2 s 2\ \mathrm{s} 2 s)
编号 1 ∼ n 1\sim n 1∼n的 n n n个博主分别有 a 1 , ⋯ , a n a_1,\cdots,a_n a1,⋯,an个粉丝,假定任一粉丝只关注一个博主.现要请 k k k个博主,使得所有粉丝数之和最大,求方案数,答案对 1 e 9 + 7 1\mathrm{e}9+7 1e9+7取模.
有 t ( 1 ≤ t ≤ 1000 ) t\ \ (1\leq t\leq 1000) t (1≤t≤1000)组测试数据.每组测试数据第一行输入两个整数 n , k ( 1 ≤ k ≤ n ≤ 1000 ) n,k\ \ (1\leq k\leq n\leq 1000) n,k (1≤k≤n≤1000).第二行输入 n n n个整数 a 1 , ⋯ , a n ( 1 ≤ a i ≤ n ) a_1,\cdots,a_n\ \ (1\leq a_i\leq n) a1,⋯,an (1≤ai≤n).数据保证所有测试数据的 n n n之和不超过 1000 1000 1000.
思路
将博主按粉丝数降序排列,贪心地选前 k k k个博主.若遇到粉丝数相同的博主,组合数即可.
代码
const int MAXN = 1005;
const int MOD = 1e9 + 7;
int fac[MAXN], ifac[MAXN];
void init() { // 预处理阶乘及其逆元
fac[0] = 1;
for (int i = 1; i < MAXN; i++) fac[i] = (ll)fac[i - 1] * i % MOD;
ifac[MAXN - 1] = qpow(fac[MAXN - 1], MOD - 2, MOD);
for (int i = MAXN - 1; i; i--) ifac[i - 1] = (ll)ifac[i] * i % MOD;
}
int C(int n, int m) { // 组合数C(n,m)
return (ll)fac[n] * ifac[m] % MOD * ifac[n - m] % MOD;
}
void solve() {
int n, k; cin >> n >> k;
vi cnt(n + 1);
for (int i = 0; i < n; i++) {
int a; cin >> a;
cnt[a]++;
}
for (int i = n; i >= 0; i--) {
if (cnt[i] >= k) {
cout << C(cnt[i], k) << endl;
return;
}
else k -= cnt[i];
}
}
int main() {
init();
CaseT // 单测时注释掉该行
solve();
}
1513B. AND Sequences
原题指路:https://codeforces.com/problemset/problem/1513/B
题意 ( 2 s 2\ \mathrm{s} 2 s)
称一个长度为 n ( n ≥ 2 ) n\ \ (n\geq 2) n (n≥2)的序列 a 1 , ⋯ , a n a_1,\cdots,a_n a1,⋯,an是好的,如果对 ∀ i ∈ [ 1 , n − 1 ] \forall i\in [1,n-1] ∀i∈[1,n−1],都有 a 1 & ⋯ & a i = a i + 1 & ⋯ & a n a_1\& \cdots\& a_i=a_{i+1}\& \cdots \& a_n a1&⋯&ai=ai+1&⋯&an.给定一个序列 a 1 , ⋯ , a n a_1,\cdots,a_n a1,⋯,an,求有多少个 1 ∼ n 1\sim n 1∼n的排列 p 1 , ⋯ , p n s . t . a p 1 , ⋯ , a p n p_1,\cdots,p_n\ s.t.\ a_{p_1},\cdots,a_{p_n} p1,⋯,pn s.t. ap1,⋯,apn是好的,答案对 1 e 9 + 7 1\mathrm{e}9+7 1e9+7取模.
有 t ( 1 ≤ t ≤ 1 e 4 ) t\ \ (1\leq t\leq 1\mathrm{e}4) t (1≤t≤1e4)组测试数据.每组测试数据第一行输入一个整数 n ( 2 ≤ n ≤ 2 e 5 ) n\ \ (2\leq n\leq 2\mathrm{e}5) n (2≤n≤2e5).第二行输入 n n n个整数 a 1 , ⋯ , a n ( 0 ≤ a i ≤ 1 e 9 ) a_1,\cdots,a_n\ \ (0\leq a_i\leq 1\mathrm{e}9) a1,⋯,an (0≤ai≤1e9).数据保证所有测试数据的 n n n之和不超过 2 e 5 2\mathrm{e}5 2e5.
思路
若序列 a 1 , ⋯ , a n a_1,\cdots,a_n a1,⋯,an是好的,则 a 1 = a 2 & ⋯ & a n a_1=a_2\& \cdots\& a_n a1=a2&⋯&an,两边与 a 1 a_1 a1得 a 1 = a 1 & ⋯ & a n a_1=a_1\& \cdots\& a_n a1=a1&⋯&an,同理 a n = a 1 & ⋯ & a n a_n=a_1\& \cdots\& a_n an=a1&⋯&an,即序列首尾的数只能是序列所有元素的与.
设 a 1 = a n = a 1 & ⋯ & a n = x a_1=a_n=a_1\& \cdots\& a_n=x a1=an=a1&⋯&an=x,则 x ≤ min { a 1 , ⋯ , a n } x\leq \min\{a_1,\cdots,a_n\} x≤min{a1,⋯,an}.显然确定 a 1 a_1 a1和 a n a_n an后,任一前缀与、后缀与都分别由 a 1 a_1 a1、 a n a_n an决定,故中间的 ( n − 2 ) (n-2) (n−2)个元素可任意排列.设序列中值为 x x x的元素的个数为 c n t cnt cnt,则 a n s = A x 2 ⋅ ( n − 2 ) ! ans=A_x^2\cdot (n-2)! ans=Ax2⋅(n−2)!.
代码
const int MAXN = 2e5 + 5;
const int MOD = 1e9 + 7;
int fac[MAXN], ifac[MAXN];
void init() { // 预处理阶乘及其逆元
fac[0] = 1;
for (int i = 1; i < MAXN; i++) fac[i] = (ll)fac[i - 1] * i % MOD;
ifac[MAXN - 1] = qpow(fac[MAXN - 1], MOD - 2, MOD);
for (int i = MAXN - 1; i; i--) ifac[i - 1] = (ll)ifac[i] * i % MOD;
}
int A(int n, int m) { // 排列数A(n,m)
if (n < m) return 0;
else return (ll)fac[n] * ifac[n - m] % MOD;
}
void solve() {
int n; cin >> n;
vi a(n);
int tmp = INT_MAX;
for (auto& ai : a) {
cin >> ai;
tmp &= ai;
}
int cnt = count(all(a), tmp);
cout << (ll)A(cnt, 2) * fac[n - 2] % MOD << endl;
}
int main() {
init();
CaseT // 单测时注释掉该行
solve();
}