題面
\(T\) 組測試數據。給定字符串 \(S\),問有多少不同的非空字符串 \(A\),\(B\),\(C\) 滿足 \(S=ABABAB...ABC\) 且 \(A\) 中出現奇數次的字符數不多於 \(C\)。
數據范圍:\(1\le T\le 5\),\(1\le |S|\le 2^{20}\)。
這估計是我場上唯一做出來的題目了,NOIP2020 游記 也不放洛谷博客里了。
提供一個 \(\Theta(n)\) 的做法,下標從 \(0\) 開始。
求出 \(S\) 的 Z
數組,\(ze_i\) 表示滿足 \(s[0,ze_i)=s[i,i+ze_i)\) 的最大值。
根據 \(s[0,ze_i)=s[i,i+ze_i)\) 可以推出 \(s[i,ze_i)=s[2i,i+ze_i)\)。
所以可以枚舉 \(AB\) 的長度 \(i\),易得這個串在前綴重復了 \(\lfloor\frac{ze_i}{i}\rfloor+1\) 次。
然后解決關於出現奇數次的字符的限制。
維護一個樹狀數組,表示每個 \(j<i\) 的前綴出現奇數次字符數的集合。
計算前綴出現奇數次字符數可以 \(\Theta(1)\),值域為字符集的樹狀數組時間復雜度也是 \(\Theta(1)\) 的。
考慮對於當前的所有 \(ABAB...AB\) 對應的 \(C\) 中出現奇數次字符數:只有兩種可能。
一種是當前后綴出現奇數次字符數,一種是全局出現奇數次字符數。先預處理后者,前者可以 \(\Theta(1)\) 維護。
因為 \(ABAB\) 中出現奇數次字符數肯定為 \(0\),所以對於 \(\lfloor\frac {\lfloor\frac{ze_i}{i}\rfloor+1}{2}\rfloor\) 的肯定是全局次數,\(\lceil\frac {\lfloor\frac{ze_i}{i}\rfloor+1}{2}\rceil\) 的肯定是后綴次數。
然后對於兩種情況,答案加上樹狀數組查詢前綴滿足條件的 \(A\) 的數量乘以出現次數即可。
所以總時間復雜度 \(\Theta(n)\)。
代碼
回家手敲的。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define x first
#define y second
#define bg begin()
#define ed end()
#define pb push_back
#define mp make_pair
#define sz(a) int((a).size())
#define R(i,n) for(int i(0);i<(n);++i)
#define L(i,n) for(int i((n)-1);i>=0;--i)
const int iinf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f;
//Data
const int N=1<<20,C=26;
int n,all,last,now,tot[C],cnt[C];
string str;
//FenwickTree
int fen[C|1];
void add(int i,int v){for(;i<C+1;i|=i+1) fen[i]+=v;}
int sum(int i,int v=0){for(;i>=0;(i&=i+1)--) v+=fen[i]; return v;}
//Zebra
int ze[N];
void zebra(){
int l=0;
R(i,n) ze[i]=0;
R(i,n)if(i){
if(l+ze[l]>i) ze[i]=min(l+ze[l]-i,ze[i-l]);
while(i+ze[i]<n&&str[ze[i]]==str[i+ze[i]]) ze[i]++;
if(i+ze[i]>l+ze[l]) l=i;
}
}
//Main
void Main(){
cin>>str,n=sz(str),zebra();
R(i,n)if(i+ze[i]==n) ze[i]--;
ll ns=all=last=now=fen[C]=0;
R(c,C) tot[c]=cnt[c]=fen[c]=0;
R(i,n) tot[str[i]-'a']++;
R(c,C) all+=(tot[c]&1); last=all;
R(i,n){
if(tot[str[i]-'a']&1) last--; else last++;
if(cnt[str[i]-'a']&1) now--; else now++;
tot[str[i]-'a']--,cnt[str[i]-'a']++;
if(i&&i<n-1){
int t=ze[i+1]/(i+1)+1;
// cout<<"i="<<i<<" t="<<t<<'\n';
ns+=1ll*(t/2)*sum(all)+1ll*(t-t/2)*sum(last);
}
// cout<<"now="<<now<<" last="<<last<<" all="<<all<<'\n';
add(now,1);
}
cout<<ns<<'\n';
}
int main(){
//freopen("string.in","r",stdin);
//freopen("string.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int T; cin>>T;
while(T--) Main();
return 0;
}
祝大家學習愉快!