1. 程式人生 > >luogu題解 UVA11536 【Smallest Sub-Array】最短set區間&滑動窗口

luogu題解 UVA11536 【Smallest Sub-Array】最短set區間&滑動窗口

給定 urn 其中 區間 lib AR ring clas tdi

  • 題目鏈接:

https://www.luogu.org/problemnew/show/UVA11536

  • 題目大意:

給定一個\(N,M,K\),構造這樣的數列:

\(x[1]=1,x[2]=2,x[3]=3\)
\(x[i]=(x[i-1]+x[i-2]+x[i-3])\mod M+1(N>=i>=4)\)

然後問你是否存在一個在\(x[1]\)\(x[n]\)中的區間,使得\([1,K]\)所有元素在其中至少出現過一次。

若存在,輸出這個區間最短長度;否則輸出\("sequence\) \(nai"\)

有T組數據

  • 分析:

我們可以想到用滑動窗口的改進做法。

搞一個左指針\(l\)

,右指針\(r\),和一個\(cnt\)數組記錄每個元素相對應在\([l,r]\)中的出現次數。

然後不斷嘗試右移\(r\),同時將\(cnt[x[r]]++\),如果\(cnt[x[r]]==1\),就讓\(done++\),表示多了一個新元素出現在區間內,如果\(done==k\),說明所有元素出現過一次,這時嘗試右移左指針,用上面類似的操作,只不過判定有所區別,直到\(done!=k\)說明不滿足性質,記錄下此時\(r-l\)長度,再重復右移\(r\)的操作

其實如果你對莫隊有了解的話這些應該很容易理解

  • 註意:

慶幸的是由於\(\mod M\)的限制,數列中的數都很小,可以直接記錄,不必用map或離散化

  • 代碼:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cctype>
#define inf 0xfffffff
using namespace std;
const int maxn=1000009;
int t,n,m,k;
int f[maxn];
int cnt[1005];
int main(){
    scanf("%d",&t);
    for(register int ite=1;ite<=t;ite++){
        memset(cnt,0,sizeof(cnt));
        scanf("%d %d %d",&n,&m,&k);
        f[1]=1,f[2]=2,f[3]=3;
        for(register int i=4;i<=n;i++)f[i]=(f[i-1]+f[i-2]+f[i-3])%m+1;
        int l=1,r=1,done=0,ans=inf;
        while(r<n){
            int g=f[r++];
            if(g>=1&&g<=k)cnt[g]++;
            if(cnt[g]==1)done++;
            while(done==k&&l<r){
                ans=min(ans,r-l);
                g=f[l++];
                cnt[g]--;
                if(cnt[g]==0)done--;
            }
        }
        printf("Case %d: ",ite);
        if(ans==inf)puts("sequence nai");
        else printf("%d\n",ans);
    }
    return 0;
}

luogu題解 UVA11536 【Smallest Sub-Array】最短set區間&滑動窗口