题意:你正在使用的音乐播放器有一个所谓的乱序播放功能,即随机打乱歌曲的播放顺序。假设一共有s首歌,则一开始会给这s首歌随机排序,全部播放完毕后再重新随机排序、继续播放,依次类推。注意,当s首歌播放完毕之前不会重新排序。这样,播放记录里的每s首歌都是1~s的一个排列。给出一个长度为n(1≤s,n≤100000)的播放记录(不一定是从最开始记录的)xi(1≤xi≤s),你的任务是统计下次随机排序所发生的时间有多少种有多少种可能性。例如,s=4,播放记录是3,4,4,1,3,2,1,2,3,4,不难发现只有一种可能性:前两首是一个段的最后两首歌,后面是两个完整的段,因此答案是1;当s=3时,播放记录1,2,1有两种可能:第一首是一个段,后两首是另一段;前两首是一段,最后一首是另一段。答案为2。
思路:这个题运用滑动窗口的思想,在O(n)的复杂度下,计算出所有的窗口中是不是有重复的元素,然后最后判断出所有的可行点。
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <fstream>
#include <iostream>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <vector>
#define MAXN 300010
#define MAXE 210
#define INF 10000000
#define MOD 1000000007
#define LL long long
#define pi acos(-1.0)
using namespace std;
int arr[MAXN];
int vis[MAXN];
bool check[MAXN];
int main() {
std::ios::sync_with_stdio(false);
int T;
cin >> T;
for (int kase = 1; kase <= T; ++kase) {
memset(arr, -1, sizeof(arr));
memset(vis, 0, sizeof(vis));
memset(check, false, sizeof(check));
int n, m;
cin >> m >> n;
for (int i = m; i < n + m; ++i)
cin >> arr[i];
int cnt = 0;
for (int i = m; i < 2 * m + n; ++i) {
if (arr[i - m] != -1) {
vis[arr[i - m]]--;
if (vis[arr[i - m]] == 1)
cnt--;
}
if (arr[i] != -1) {
vis[arr[i]]++;
if (vis[arr[i]] > 1)
cnt++;
}
if (cnt == 0) {
check[i - m] = true;
}
}
int ans = 0;
for (int i = 0; i < m; ++i) {
bool flag = true;
for (int j = i; j < m + n; j += m) {
if (!check[j]) {
flag = false;
break;
} else {
check[j] = false;
}
}
if (flag)
ans++;
}
cout << ans << endl;
}
return 0;
}
/*
4
4 10
3 4 4 1 3 2 1 2 3 4
6 6
6 5 4 3 2 1
3 5
3 3 1 1 1
7 3
5 7 3
*/