P3498 [POI2010] KOR-Beads
對於數列 ${A_i}$ ,求 $k$ 使數列被分為 $\lfloor \frac{n}{k}\rfloor$ 個部分後不同子數列種類最多(子串翻轉前後為一類子串)
-
很容易想到:列舉 $k\ \in\ [1,n]$ ,記錄每個 $k$ 下不同種類數,然後取最優即可,那麼問題來了:如何計算種類數?
-
暴戾演算法:
一種純粹的暴戾演算法(霧)便是:對於每個 $k$ 使用
map
與string
記錄當前串是否出現過,但問題在於字串需要不斷改變,即需要 $2e5$ 的string
變數,無論是時間複雜度還是空間複雜度都是不可接受的。 -
正解雜湊:
因此我們直接使用
hash
演算法,並選擇map
或者set
判重。/*code by CheemsaDoge*/ #include <bits/stdc++.h>//用了萬能頭,我就是嘯紂老師口中的低水平(哭 template<typename T> inline void read(T &x) {/*...*/}//fast input template<typename T> inline void write(T x){/*...*/} const int MAXM=1e6+1145;const int MAXN=200000+11145;const int INF=2147483647ll;//2^31-1 #define pc putchar('\n') #define pk putchar(' ') #define ull unsigned long long /*---------------------------------pre------------------------------------*/ std::set<ull>hash;//直接用unsigned long long讓他自己溢位來 const ull sp=19260817; ull s1[MAXN],s2[MAXN],pp[MAXN]; int a[MAXN],n; ull get1(int l,int r) {return s1[r]-s1[l-1]*pp[r-l+1];} //獲得hash值(從前往後計算 ull get2(int l,int r) {return s2[l]-s2[r+1]*pp[r-l+1];} //獲得hash值(從後往前計算 template <typename T> T min(const T _a,const T _b) {return _a<_b?_a:_b;} //手寫 min 函式 int get_ans(int k) { hash.clear(); for(int i=0;i*k+k<=n;i++) hash.insert(min<ull>(get1(i*k+1,i*k+k),get2(i*k+1,i*k+k))); //插入當前字串(子串)的hash值 return hash.size(); //返回種類數(set不允許出現相同元素,所以前面可以安心插 }//得到答案 std::vector<int>ans;//動態陣列儲存滿足條件的k int maxn=-1;//最大的種類數 int main(){ read(n); for(int i=1;i<=n;i++) read(a[i]); pp[0]=1; for(int i=1;i<=n;i++) { s1[i]=1ull*a[i]+s1[i-1]*sp; pp[i]=pp[i-1]*sp; } for(int i=n;i>=1;i--) s2[i]=a[i]+s2[i+1]*sp;//初始化hash for(int i=1;i<=n;i++) { int ma=get_ans(i);//獲得k的種類數 if(maxn<ma) { //更新答案 ans.clear(); ans.push_back(i); maxn=ma; } else if(maxn==ma) ans.push_back(i); } write(maxn);pk;write(ans.size());pc;//輸出(pc是putchar('\n'),前面的宏有定義 for(int i=0;i<(int)ans.size();i++) write(ans[i]),pk;//pk是putchar(' ') return 0; //好習慣捏 }
關於模數:這道題如果
sp
被設為10007
親測會掛,機房裡的 $dalao$ 就有這麼掛的。所以一個好的模數是必不可少的,建議用
13331
或者某人的生日19260817
。 -