UVa OJ 12174 - Shuffle

Problem

You are listening to your music collection using the shuffle function to keep the music surprising. You assume that the shuffle algorithm of your music player makes a random permutation of the songs in the playlist and plays the songs in that order until all songs have been played. Then it reshuffles and starts playing the list again.
  You have a history of the songs that have been played. However, your record of the history of played songs is not complete, as you started recording songs at a certain point in time and a number of songs might already have been played. From this history, you want to know at how many different points in the future the next reshuffle might occur.
  A potential future reshuffle position is valid if it divides the recorded history into intervals of length s (the number of songs in the playlist) with the rst and last interval possibly containing less than s songs and no interval contains a specic song more than once.

Input

On the rst line one positive number: the number of testcases, at most 100. After that per testcase:
• One line with two integers s and n (1 ≤ s, n ≤ 100000): the number of different songs in the playlist and the number of songs in the recorded playlist history.
• One line with n space separated integers, x1, x2, … , xn (1 ≤ xi ≤ s): the recorded playlist history.

Output

One line with the number of future positions the next reshuffle can be at. If the history could not be generated by the above mentioned algorithm, output 0.

Sample Input

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

Sample Output

1
6
0
7

Solution

一开始没想太多,直接用枚举的方式,写了一下代码,提交之后虽然AC了,但是耗时2s多,内心还是不舒服的。于是尝试改了一下,也参考了别人的代码。

总体说来还是一道滑动窗口的题目,并不是十分复杂,但是处理得当的话,效率能提升很多。这里为了节省时间,通过一次扫描,将每一段可以构成音乐单的数字,用idx这个数组来保存,可以为1,不行则为0.最后再将第一个数字按顺序,先当成第一段的最后一个,再当成第一段的倒数第二个,以此类推,得到结果。

#include <iostream>
#include <cstring>
using namespace std;

const int maxn = 100005;
int ans, cas, s, n;
int x[3*maxn], idx[maxn<<1], cnt[maxn];
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> cas;
    while (cas--){
        cin >> s >> n;
        memset(x, 0, sizeof(int)*(n+(s<<1)+1));
        memset(cnt, 0, sizeof(int)*(s+1));
        memset(idx, 0, sizeof(int)*(n+s+1));
        for (int i = 0; i < n; ++i) cin >> x[i+s];

        int tot = 0;
        for (int i = 0; i < n + s + 1; ++i){
            if ((tot == s) || (i < s && tot == i) || (i > n && tot == n + s - i)) 
                idx[i] = 1;

            if (x[i] && !--cnt[x[i]]) --tot;
            if (x[i + s] && !cnt[x[i + s]]++) ++tot;
        }

        ans = 0;
        for (int i = 0; i < s; ++i) {
            bool isOK = true;
            for (int j = i; j < n + s + 1; j += s) {
                if (!idx[j]){
                    isOK = false;
                    break;
                }
            }
            if (isOK) ++ans;
        }
        if (ans > n) ans = s;
        cout << ans << '\n';
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值