日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

牛客多校6 - K-Bag(哈希+滑动窗口)

發布時間:2024/4/11 编程问答 50 豆豆
生活随笔 收集整理的這篇文章主要介紹了 牛客多校6 - K-Bag(哈希+滑动窗口) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

題目鏈接:點擊查看

題目大意:k-bag 序列的定義是由多個 1 ~ k 的排列順序連接起來的序列,現在問給定的序列是不是 k-bag 的連續子串

題目分析:讀完題目后,如果給定的序列是 k-bag 的連續子串的話,那么可以將所有情況都分為以下三種類型:

  • 一個部分k-bag的前綴 + 數個(至少一個)完整的k-bag + 一個部分k-bag的后綴
  • 一個部分k-bag的前綴 + 一個部分k-bag的后綴
  • 一個部分k-bag
  • 判斷部分k-bag和完整的k-bag需要用到兩種不同的方法,判斷部分k-bag的話,只需要判斷區間內的每個數都是否不同即可,即區間的長度等于區間內不同的數的個數,這個利用 dp 和 unordered_map 搭配就能 O( n ) 的時間內預處理出來了,這里用 cnt1[ i ] 表示 1 ~ i 內有多少個不同的數(前綴),cnt2[ i ] 表示 i ~ n 內有多少個不同的數(后綴)

    接下來需要判斷完整的k-bag,如果也是用 “ 區間內有多少個不同的數 ” 去判斷的話,需要用到主席樹,但可能會超時(群友是這樣說的),而且代碼復雜度也大大上升,很容易寫崩,這里用到一種哈希的方法,不會證明只會用,大概就是一個連續的序列?[ l , r ] ,維護 sum 和 xor ,sum = l + ( l + 1 ) + ... + ( r - 1 ) + r ,xor = l ^ ( l + 1 ) ^ ... ^ ( r - 1 ) ^ r ,形成一個二元對 < sum , xor > ,這個二元對和 [ l , r ] 是一一對應的,可以稱之為 RSB哈希 ( rsb學長自己起的名字?)

    上面開個小玩笑,回到這個題目中來,我們可以再次 O( n ) 維護一下整個數列的 sum前綴和 和 xor前綴異或和,然后再小分一下類,當 k < n 時,只會有上面的情況 1 和情況 2 ,這樣我們可以枚舉 [ 1 , k ] 作為滑動窗口的起點,然后檢查每個長度為 k 的滑動窗口,對于每個長度為 k 的滑動窗口,我們現在可以 O( 1 ) 求出當前窗口的xor之和以及sum之和,判斷一下是否等于 1 ~ k 的 xor 和 sum 就好了,當 k >= n 時,就只有情況 2 和情況 3 了,此時我們可以當做情況 2 處理,O( n?) 枚舉一下斷點,將整個數列分割成前半部分和后半部分,如果某次分割下,前半部分和后半部分滿足了情況 2 的話,那么顯然答案就是 YES 了,相反,如果上面三種情況都不滿足的話,答案就是 NO 了

    時間復雜度為 O( n ) ,如果不計 unordered_map 的常數的話

    代碼:

    #include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cmath> #include<cstring> #include<algorithm> #include<stack> #include<climits> #include<queue> #include<map> #include<set> #include<sstream> #include<cassert> #include<bitset> #include<unordered_map> using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=5e5+100;int a[N],n,k;LL sum[N],Xor[N],SUM,XOR;unordered_map<int,int>mp;int cnt1[N],cnt2[N];//cnt1[i]:1~i內有多少個不同的數,cnt2[i]:i~n內有多少個不同的數bool check(int st)//檢查以st為起點的滑動窗口是否滿足條件 {for(int i=st;i<=n;i+=k){int l=i,r=i+k-1;if(r<=n){if(sum[r]-sum[l-1]!=SUM||(Xor[r]^Xor[l-1])!=XOR)return false;}else return cnt2[l]==n-l+1;}return true; }bool solve() {if(k<n)//情況1和情況2{ SUM=0,XOR=0;for(int i=1;i<=k;i++)//O(k)處理一下1~k的xor和sum作為標準{SUM+=i;XOR^=i;}for(int i=1;i<=k;i++)//枚舉起點位置{if(cnt1[i-1]!=i-1)break;if(check(i))return true;}}else//情況2和情況3{for(int i=2;i<=n;i++)//枚舉斷點if(cnt1[i-1]==i-1&&cnt2[i]==n-i+1)return true;}return false; }void init()//預處理cnt1和cnt2 {mp.clear();cnt1[0]=0;for(int i=1;i<=n;i++){cnt1[i]=cnt1[i-1];if(!mp[a[i]])cnt1[i]++;mp[a[i]]++;}mp.clear();cnt2[n+1]=0;for(int i=n;i>=1;i--){cnt2[i]=cnt2[i+1];if(!mp[a[i]])cnt2[i]++;mp[a[i]]++;} }int main() { #ifndef ONLINE_JUDGE // freopen("data.in.txt","r",stdin); // freopen("data.out.txt","w",stdout); #endif // ios::sync_with_stdio(false);int w;cin>>w;while(w--){scanf("%d%d",&n,&k);bool flag=true;//判斷數列的每個元素是否位于1~k之間for(int i=1;i<=n;i++){scanf("%d",a+i);if(a[i]<1||a[i]>k)flag=false;sum[i]=sum[i-1]+a[i];Xor[i]=Xor[i-1]^a[i];}if(!flag){puts("NO");continue;}init();if(solve())puts("YES");elseputs("NO");}return 0; }

    ?

    總結

    以上是生活随笔為你收集整理的牛客多校6 - K-Bag(哈希+滑动窗口)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。