F. Ira and Flamenco

F. Ira and Flamenco

Ira loves Spanish flamenco dance very much. She decided to start her own dance studio and found n students, ith of whom has level ai.

Ira can choose several of her students and set a dance with them. So she can set a huge number of dances, but she is only interested in magnificent dances. The dance is called magnificent if the following is true:

  • exactly m students participate in the dance;
  • levels of all dancers are pairwise distinct;
  • levels of every two dancers have an absolute difference strictly less than m.

For example, if m=3 and a=[4,2,2,3,6], the following dances are magnificent (students participating in the dance are highlighted in red): [4,2,2,3,6], [4,2,2,3,6]. At the same time dances [4,2,2,3,6], [4,2,2,3,6], [4,2,2,3,6] are not magnificent.

In the dance [4,2,2,3,6] only 2 students participate, although m=3.

The dance [4,2,2,3,6] involves students with levels 2 and 2, although levels of all dancers must be pairwise distinct.

In the dance [4,2,2,3,6] students with levels 3 and 6 participate, but |36|=3, although m=3.

Help Ira count the number of magnificent dances that she can set. Since this number can be very large, count it modulo 109+7. Two dances are considered different if the sets of students participating in them are different.

Input

The first line contains a single integer t (1t104) — number of testcases.

The first line of each testcase contains integers n and m (1mn2105) — the number of Ira students and the number of dancers in the magnificent dance.

The second line of each testcase contains n integers a1,a2,,an (1ai109) — levels of students.

It is guaranteed that the sum of n over all testcases does not exceed 2105.

Output

For each testcase, print a single integer — the number of magnificent dances. Since this number can be very large, print it modulo 109+7.

Example

input

复制代码
9
7 4
8 10 10 9 6 11 7
5 3
4 2 2 3 6
8 2
1 5 2 2 3 1 3 3
3 3
3 3 3
5 1
3 4 3 10 7
12 3
5 2 1 1 4 3 5 5 5 2 7 5
1 1
1
3 2
1 2 3
2 2
1 2
复制代码

output

复制代码
5
2
10
0
5
11
1
2
1
复制代码

Note

In the first testcase, Ira can set such magnificent dances: [8,10,10,9,6,11,7], [8,10,10,9,6,11,7], [8,10,10,9,6,11,7], [8,10,10,9,6,11,7], [8,10,10,9,6,11,7].

The second testcase is explained in the statements.

 

解题思路

  首先可以发现改变数组a中元素的位置并不影响答案的统计,因此可以先对数组a的元素进行升序排序。由于不能选择重复的元素,因此可以先统计每个元素出现的次数,再对排序好的数组进行进行去重。

  对于排序且去重后的数组a,根据三个约束条件可以知道,我们应该要从a中选择m个不同的元素,且任意两个元素的差值严格小于m。假设最小的元素为x,根据约束条件那么选择的元素一定满足这个形式:[x,x+1,x+2,,x+m1]。首先这里恰好选择了m个不同的元素,且任意两个元素的差值严格小于m。因此本质就是在a中选择连续一段的元素,且相邻两个元素的差值恰好为1。假设第i个元素aia中出现了ci次,那么根据乘法原理,以ai为最小值且满足约束的一段连续元素的数量就是j=ii+m1cj(mod109+7)

  因此我们可以枚举所有的元素,将其作为最小值,然后往后找连续m个元素看看是否满足约束,如果满足则统计答案。为此可以用双指针,设i为满足约束的最右边的元素,j为满足约束的最左边的元素,当i往右移时j也会往右移,同时开个变量维护这一段连续元素(即ji)对应的次数的乘积。当j往右以很明显需要对这个变量除以cj,但这是个取模的乘法,与之对应的应该是乘以在模109+7意义下cj的乘法逆元,而根据费马小定理,对应的逆元就是cj(109+7)2(mod109+7)

  AC代码如下,时间复杂度为O(nlog(109+7))

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long LL;
 5 typedef pair<int, int> PII;
 6 
 7 const int N = 2e5 + 10, mod = 1e9 + 7;
 8 
 9 int a[N];
10 map<int, int> mp;
11 
12 int qmi(int a, int k) {
13     int ret = 1;
14     while (k) {
15         if (k & 1) ret = 1ll * ret * a % mod;
16         a = 1ll * a * a % mod;
17         k >>= 1;
18     }
19     return ret;
20 }
21 
22 void solve() {
23     int n, m;
24     scanf("%d %d", &n, &m);
25     mp.clear();
26     for (int i = 0; i < n; i++) {
27         scanf("%d", a + i);
28         mp[a[i]]++;    // 统计每个元素出现的次数
29     }
30     sort(a, a + n);
31     n = unique(a, a + n) - a;    // 排序去重
32     int ret = 0, prod = 1;
33     for (int i = 0, j = 0; i < n; i++) {
34         prod = 1ll * prod * mp[a[i]] % mod;
35         while (a[i] - a[j] >= m) {    // 不满足第3个约束,即每个元素的差值应该严格小于m
36             prod = 1ll * prod * qmi(mp[a[j++]], mod - 2) % mod;    // 把c_j除掉,等价于乘以对应的乘法逆元
37         }
38         if (i - j + 1 == m) ret = (ret + prod) % mod;    // 有连续m个不同的元素
39     }
40     printf("%d\n", ret);
41 }
42 
43 int main() {
44     int t;
45     scanf("%d", &t);
46     while (t--) {
47         solve();
48     }
49     
50     return 0;
51 }
复制代码

  再给出一种用线段树维护区间取模乘法的做法。因为本质就是每次都需要快速知道某段连续区间的取模乘法是多少,因此可以用线段树来进行维护,这样就不需要用到求逆元,一开始没想到这种做法。

  AC代码如下,时间复杂度为O(nlogn)

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long LL;
 5 
 6 const int N = 2e5 + 10, mod = 1e9 + 7;
 7 
 8 int a[N];
 9 map<int, int> mp;
10 struct Node {
11     int l, r, prod;
12 }tr[N * 4];
13 
14 void build(int u, int l, int r) {
15     if (l == r) {
16         tr[u] = {l, r, mp[a[l]]};
17         
18     }
19     else {
20         int mid = l + r >> 1;
21         build(u << 1, l, mid);
22         build(u << 1 | 1, mid + 1, r);
23         tr[u] = {l, r, 1ll * tr[u << 1].prod * tr[u << 1 | 1].prod % mod};
24     }
25 }
26 
27 int query(int u, int l, int r) {
28     if (tr[u].l >= l && tr[u].r <= r) return tr[u].prod;
29     int mid = tr[u].l + tr[u].r >> 1, ret = 1;
30     if (l <= mid) ret = query(u << 1, l, r);
31     if (r >= mid + 1) ret = 1ll * ret * query(u << 1 | 1, l, r) % mod;
32     return ret;
33 }
34 
35 void solve() {
36     int n, m;
37     scanf("%d %d", &n, &m);
38     mp.clear();
39     for (int i = 1; i <= n; i++) {
40         scanf("%d", a + i);
41         mp[a[i]]++;
42     }
43     sort(a + 1, a + n + 1);
44     n = unique(a + 1, a + n + 1) - a - 1;
45     build(1, 1, n);
46     int ret = 0;
47     for (int i = 1, j = 1; i <= n; i++) {
48         while (a[i] - a[j] >= m) {
49             j++;
50         }
51         if (i - j + 1 == m) ret = (ret + query(1, j, i)) % mod;
52     }
53     printf("%d\n", ret);
54 }
55 
56 int main() {
57     int t;
58     scanf("%d", &t);
59     while (t--) {
60         solve();
61     }
62     
63     return 0;
64 }
复制代码

 

参考资料

  Codeforces Round #874 (Div. 3) Editorial:https://codeforces.com/blog/entry/116636

posted @   onlyblues  阅读(109)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
历史上的今天:
2022-05-22 奶牛摄影
2022-05-22 积木画
Web Analytics
点击右上角即可分享
微信分享提示