fl0ppy-

导航

CF1833F Ira and Flamenco题解

CF1833F Ira and Flamenco

题面

太不好想了

首先注意到 任意两名舞者的水平之差的绝对值严格小于 m,

极限法一下,假设我们有一个已经选出来的几个人,且他们的水平严格递增。

举个例子,我们有

\[A = [a_1, a_2, a_3, a_4, ...,a_m] \]

因为上述的条件,最大的 \(a_m\) 和 \(a_1\) 也得满足 \(|a_1 - a_m| < m\)。

又因为他们是正整数,也即 \(a_m \le a_1 + m - 1\)。

又因为所有水平值都是整数,并且互不相同,所以相邻两个至少相差 1:

也就是说

\[a_i - a_{i-1} \ge 1 \quad (i = 2,3,4,...,m) \]

加起来得到“最大值 - 最小值”的另一种写法:

\[a_m - a_1 = (a_m - a_{m-1}) + ... + (a_2 - a_1) \ge 1 \times (m-1) = m - 1 \]

合并就有了

\[m - 1 \le a_m - a_1 < m \]

那只能取 \(a_m - a_1 = m - 1\) 了。最大减最小是 \(m-1\),而它又是 \(m-1\) 个相邻差之和,且每一个相邻差都至少为 1,形式化表达如下:

\[a_m - a_1 = \sum_{i=2}^{m} (a_i - a_{i-1}) = m-1 \]

哪怕有一个相邻数之差比 1 大,整个和就不可能是 \(m-1\),故每一个数相邻之差只能是 1。

也就是说,选出来的人能力值只能是:

\[A = [a_1, a_1+1, a_1+2, ..., a_1+m-1] \]

然后就好做了,开个桶,在给出的能力值里找连续的且长度大于等于 \(m\) 的一段,再在段里找窗口。

假设我们找到了一段

\[val = [v, v+1, v+2, v+3, ..., v+L-1] \]

其中每个值 \(v+i\) 出现了 \(cnt[v+i]\) 次。

对于其中的一个长为 \(m\) 的窗口:

\[[v+i, v+i+1, v+i+2, ..., v+i+m-1] \]

必须分别在水平 \(v+i\),水平 \(v+i+1\),...,水平 \(v+i+m-1\) 里各选一个,考虑乘法原理,这个窗口能组成的壮丽舞蹈数量就是:

\[\prod_{k=0}^{m-1} cnt[v+i+k] \]

即窗口内每个值的出现次数相乘。

把所有合法窗口的方案数累加起来就是答案。但直接枚举每个窗口并重新计算乘积复杂度不太妙,会t。所以用滑动窗口维护乘积。

令当前窗口的乘积为 \(prod\),窗口为 \([v+i, v+i+m-1]\)。窗口向右滑动一格,变成 \([v+i+1, v+i+m]\),新的乘积为:

\[prod' = prod \times cnt[v+i]^{-1} \times cnt[v+i+m] \pmod{10^9+7} \]

其中 \(cnt[v+i]^{-1}\) 是 \(cnt[v+i]\) 在模 \(10^9+7\) 下的乘法逆元。因为 \(10^9+7\) 是质数,根据费马小定理:

\[a^{-1} \equiv a^{10^9+5} \pmod{10^9+7} \]

快速幂计算即可。

具体实现时,先统计每个水平值的出现次数(可以用 map 排序后统计),然后提取所有不同的水平值并排序。接着遍历这些值,找出所有连续段。对于每个长度 \(L \ge m\) 的连续段:

  1. 计算第一个窗口 \([v, v+m-1]\) 的乘积,累加到答案;
  2. 从 \(i = v+m\) 开始向右滑动,每次乘上离开窗口的值的逆元,再乘上新进入窗口的值,得到新窗口乘积,累加到答案;
  3. 直到窗口右端点到达连续段末尾。

注意取模。

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;
	}
}

Submission

posted on 2026-10-15 18:41  fl0ppy-  阅读(7)  评论(0)    收藏  举报