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 students, th of whom has level .
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 students participate in the dance;
- levels of all dancers are pairwise distinct;
- levels of every two dancers have an absolute difference strictly less than .
For example, if and , the following dances are magnificent (students participating in the dance are highlighted in red): , . At the same time dances , , are not magnificent.
In the dance only students participate, although .
The dance involves students with levels and , although levels of all dancers must be pairwise distinct.
In the dance students with levels and participate, but , although .
Help Ira count the number of magnificent dances that she can set. Since this number can be very large, count it modulo . Two dances are considered different if the sets of students participating in them are different.
Input
The first line contains a single integer () — number of testcases.
The first line of each testcase contains integers and () — the number of Ira students and the number of dancers in the magnificent dance.
The second line of each testcase contains integers () — levels of students.
It is guaranteed that the sum of over all testcases does not exceed .
Output
For each testcase, print a single integer — the number of magnificent dances. Since this number can be very large, print it modulo .
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: , , , , .
The second testcase is explained in the statements.
解题思路
首先可以发现改变数组中元素的位置并不影响答案的统计,因此可以先对数组的元素进行升序排序。由于不能选择重复的元素,因此可以先统计每个元素出现的次数,再对排序好的数组进行进行去重。
对于排序且去重后的数组,根据三个约束条件可以知道,我们应该要从中选择个不同的元素,且任意两个元素的差值严格小于。假设最小的元素为,根据约束条件那么选择的元素一定满足这个形式:。首先这里恰好选择了个不同的元素,且任意两个元素的差值严格小于。因此本质就是在中选择连续一段的元素,且相邻两个元素的差值恰好为。假设第个元素在中出现了次,那么根据乘法原理,以为最小值且满足约束的一段连续元素的数量就是。
因此我们可以枚举所有的元素,将其作为最小值,然后往后找连续个元素看看是否满足约束,如果满足则统计答案。为此可以用双指针,设为满足约束的最右边的元素,为满足约束的最左边的元素,当往右移时也会往右移,同时开个变量维护这一段连续元素(即到)对应的次数的乘积。当往右以很明显需要对这个变量除以,但这是个取模的乘法,与之对应的应该是乘以在模意义下的乘法逆元,而根据费马小定理,对应的逆元就是。
AC代码如下,时间复杂度为:
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代码如下,时间复杂度为:
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
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/17419702.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2022-05-22 奶牛摄影
2022-05-22 积木画