CF1833F Ira and Flamenco题解
CF1833F Ira and Flamenco
太不好想了
首先注意到 任意两名舞者的水平之差的绝对值严格小于 m,
极限法一下,假设我们有一个已经选出来的几个人,且他们的水平严格递增。
举个例子,我们有
因为上述的条件,最大的 \(a_m\) 和 \(a_1\) 也得满足 \(|a_1 - a_m| < m\)。
又因为他们是正整数,也即 \(a_m \le a_1 + m - 1\)。
又因为所有水平值都是整数,并且互不相同,所以相邻两个至少相差 1:
也就是说
加起来得到“最大值 - 最小值”的另一种写法:
合并就有了
那只能取 \(a_m - a_1 = m - 1\) 了。最大减最小是 \(m-1\),而它又是 \(m-1\) 个相邻差之和,且每一个相邻差都至少为 1,形式化表达如下:
哪怕有一个相邻数之差比 1 大,整个和就不可能是 \(m-1\),故每一个数相邻之差只能是 1。
也就是说,选出来的人能力值只能是:
然后就好做了,开个桶,在给出的能力值里找连续的且长度大于等于 \(m\) 的一段,再在段里找窗口。
假设我们找到了一段
其中每个值 \(v+i\) 出现了 \(cnt[v+i]\) 次。
对于其中的一个长为 \(m\) 的窗口:
必须分别在水平 \(v+i\),水平 \(v+i+1\),...,水平 \(v+i+m-1\) 里各选一个,考虑乘法原理,这个窗口能组成的壮丽舞蹈数量就是:
即窗口内每个值的出现次数相乘。
把所有合法窗口的方案数累加起来就是答案。但直接枚举每个窗口并重新计算乘积复杂度不太妙,会t。所以用滑动窗口维护乘积。
令当前窗口的乘积为 \(prod\),窗口为 \([v+i, v+i+m-1]\)。窗口向右滑动一格,变成 \([v+i+1, v+i+m]\),新的乘积为:
其中 \(cnt[v+i]^{-1}\) 是 \(cnt[v+i]\) 在模 \(10^9+7\) 下的乘法逆元。因为 \(10^9+7\) 是质数,根据费马小定理:
快速幂计算即可。
具体实现时,先统计每个水平值的出现次数(可以用 map 排序后统计),然后提取所有不同的水平值并排序。接着遍历这些值,找出所有连续段。对于每个长度 \(L \ge m\) 的连续段:
- 计算第一个窗口 \([v, v+m-1]\) 的乘积,累加到答案;
- 从 \(i = v+m\) 开始向右滑动,每次乘上离开窗口的值的逆元,再乘上新进入窗口的值,得到新窗口乘积,累加到答案;
- 直到窗口右端点到达连续段末尾。
注意取模。
Code:
#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int N = 2e5 + 10;
inline int read(){
int x = 0; bool f = 1; char c = getchar();
for(; !isdigit(c); c = getchar())if(c == '-')f = 0;
for(; isdigit(c); c = getchar())x = (x << 1) + (x << 3) + (c ^ 48);
return f ? x : -x;
}
int arr[N];
const int mod = 1e9 + 7;
inline int ksm(int n, int m){
int res = 1;
while(m){
if(m & 1){
res = res * n % mod;
}
m >>= 1;
n = n * n % mod;
}
return res;
}
main(){
int Q = read();
while(Q--){
int n = read(), m = read();
map<int, int> mp;//记录出现几次
for(int i = 1; i <= n; ++i){
arr[i] = read();
mp[arr[i]]++;
}
vector<int> a;
for(const auto tmp : mp){
a.push_back(tmp.first);
}//去重
stable_sort(a.begin(), a.end());
int l = 0, ans = 0;
while(l < a.size()){
int r = l;
while(a[r + 1] - a[r] == 1 && r + 1 < a.size()){
r++;
}
if(r - l + 1 >= m){
int prod = 1;
for(int j = l; j < l + m; ++j){
prod = prod * mp[a[j]] % mod;
}
ans += (prod + mod);
ans %= mod;
for(int i = l + m; i <= r; ++i){
prod = prod * ksm(mp[a[i - m]], mod - 2) % mod;//费马小定理
prod = prod * mp[a[i]] % mod;
ans = (ans + prod) % mod;
}
}
l = r + 1;
}
cout << ans << endl;
}
}
浙公网安备 33010602011771号