题意:你正在使用的音乐播放器有一个所谓的乱序播放功能,即随机打乱歌曲的播放顺序。假设一共有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。(本段摘自《算法竞赛入门经典(第2版)》)
分析:
滑动窗口的思想,维护长度为s的窗口看是否合法,最后枚举可能答案进行判断即可。
代码:
这#include <fstream>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <stack>
#include <sstream>
#include <string>
#include <map>
#include <cmath>
#include <queue>
#include <vector>
#include <set>
#include <string>
#include <vector>
using namespace std;
const int maxn = 100000 + 5;
int T, ans, cnt, s, n;
int a[maxn * 3], num[maxn], x[maxn * 2];
bool flag;
int main()
{
scanf("%d", &T);
for (int C = 0; C < T; ++C)
{
ans = 0;
memset(a, -1, sizeof(a));
memset(x, 0, sizeof(x));
memset(num, 0, sizeof(num));
scanf("%d%d", &s, &n);
cnt = s;
for (int i = s; i < s + n; ++i)
scanf("%d", &a[i]);
for (int i = s; i < s + s + n; ++i)
{
if (a[i - s] != -1)
{
--num[a[i - s]];
if (num[a[i - s]] == 0)
--cnt;
}
else
--cnt;
if (a[i] != -1)
{
++num[a[i]];
if (num[a[i]] == 1)
++cnt;
}
else
++cnt;
if (cnt == s)
x[i - s] = 1;
}
for (int i = 0; i < s; ++i)
{
flag = true;
for (int j = i; j < s + n; j += s)
if (!x[j])
{
flag = false;
break;
}
if (flag)
++ans;
}
printf("%d\n", ans);
}
return 0;
}