luogu題解 UVA11536 【Smallest Sub-Array】最短set區間&滑動窗口
阿新 • • 發佈:2018-05-27
給定 urn 其中 區間 lib AR ring clas tdi ,右指針\(r\),和一個\(cnt\)數組記錄每個元素相對應在\([l,r]\)中的出現次數。
- 題目鏈接:
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[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區間&滑動窗口