令 \(a[l:r]\) 表示 \(a\) 這個 字串/陣列 的第 \(l\) 到第 \(r\) 個元素組成的 字串/陣列。類似的:link。
F
考慮令 \(k=k0\),如何判斷是否可行。
只需要對 \(T\) 從前到後一個一個字元滿足,若無法滿足則不可行。
具體的,即假設現在判斷字元 \(c\),從第 \(q\) 次迴圈的字串 \(S\) 的第 \(p\) 位開始。
令還需要在 \(S\) 中找到 \(c\) 的個數為 \(b\),\(cnt_c\ s\) 表示 \(s\) 中 \(c\) 的個數。
如果 \(b > cnt_c\ s\),那麼一直向後跳躍一整個字串(即 \(q\gets q + 1\)),將 \(b\) 減去 \(cnt_c\ s\)。
考慮如果可以在 \(s[p:|S|]\) 這個字串裡滿足,那麼直接將 \(p\) 跳躍到某個 \(c\) 的下一個位置 \(p'\),滿足 \(cnt_c\ s[p:p']=b\) 即可。
否則令 \(b\gets b-cnt_c\ s[p:|S|]\),再令 \(q\gets q+1,p\gets 1\)。
令 \(pos_c\ x\) 表示滿足 \(cnt_c\ s[1:i]=x\) 的最小的 \(i\)。
將 \(p\gets (pos_c\ b)+1\) 即可。
若最後 \(q>n\),則不可行。
注意到答案顯然單調,二分答案即可。複雜度 \(O(|S|+|T|\log n)\)。
G
看錯題卡了好久。注意是至少有一個滿足只出現一次而不是 恰好 有一個數只出現一次。
考慮從右向左掃描,若掃到 \(i\):
令 \(a[i:n]\) 中 \(x\) 第 \(i\) 次出現的位置為 \(pi_x\)。
則 \(a[i:n]\) 可表示為 \(p1_{a_i},p1_{a_{i+1}},p2_{a_i}\cdots\)(只是一個例子)。
若將 \(p1_x\) 替換為 \(+1\),\(p2_x\) 替換為 \(-1\),其他為 \(0\),則 \(a[i:n]\) 變為 \(+1\ +1\ -1\ 0\cdots\)(只是一個例子)。
舉例:若 \(a[i:n]=\{1,2,3,2,3,4,1,1\}\):
第一次用 \(pi_x\) 替換為 \(\{p1_1,p1_2,p1_3,p2_2,p2_3,p1_4,p2_1,p3_1\}\)。
接下來變為 \(\{+1,+1,+1,-1,-1,+1,-1,0\}\)。
注意到做一個字首和後對於任意的 \(a[i:n]\) 沒有數 \(<0\),並且不為 \(0\) 的數的個數即為 \(L=i\) 時 \(R\) 的方案數,考慮維護做字首和後的數列。
考慮類似於掃描線的方法,用線段樹維護區間最小值和最小值的個數,因為最小值 \(\ge 0\)(和掃描線類似),所以方便統計。
每次向左加一個數時只需要考慮 \(pi_x\) 的變化並且區間修改字首和就行了。複雜度 \(O(n\log n)\)。
程式碼
F
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int ll
const int N = 1e5 + 5, K = 26;
char s[N], t[N]; int n, sn, st;
int pos[N][K], suf[N][K], pre[N][K];
int cnt[26];
bool chk(int k)
{
int p = 1, q = 1;
for(int i = 1; i <= st; i ++)
{
int c = t[i] - 'a';
if(!cnt[c]) return false;
int k0 = k;
if(k0 > cnt[c])
q += (k0 - 1) / cnt[c], k0 = k0 - (k0 - 1) / cnt[c] * cnt[c];
if(suf[p][c] < k0) k0 -= suf[p][c], p = 1, q ++;
p = pos[pre[p - 1][c] + k0][c] + 1;
if(p > sn) p = 1, q ++;
}
if(p == 1) q --;
return q <= n;
}
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);
cin >> n >> s + 1 >> t + 1;
sn = strlen(s + 1);
st = strlen(t + 1);
for(int i = sn; i >= 1; i --)
{
cnt[s[i] - 'a'] ++;
for(int j = 0; j < K; j ++) suf[i][j] = cnt[j];
}
memset(cnt, 0, sizeof cnt);
for(int i = 1; i <= sn; i ++)
{
cnt[s[i] - 'a'] ++;
pos[cnt[s[i] - 'a']][s[i] - 'a'] = i;
for(int j = 0; j < K; j ++) pre[i][j] = cnt[j];
}
int l = 0, r = n * sn / st;
while(l < r)
{
int mid = l + r + 1 >> 1;
if(chk(mid)) l = mid;
else r = mid - 1;
}
cout << l;
return 0;
}
G
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int ll
const int N = 2e5 + 5;
int n, a[N];
int nxt[N], pos[N], ans;
struct sgt
{
int a[N << 2], c[N << 2], tg[N << 2];
void pu(int x)
{
a[x] = min(a[x << 1], a[x << 1 | 1]);
c[x] = 0;
if(a[x << 1] == a[x]) c[x] += c[x << 1];
if(a[x << 1 | 1] == a[x]) c[x] += c[x << 1 | 1];
}
void pd(int x)
{
if(tg[x])
{
a[x << 1] += tg[x], a[x << 1 | 1] += tg[x];
tg[x << 1] += tg[x], tg[x << 1 | 1] += tg[x];
tg[x] = 0;
}
}
void upd(int ql, int qr, int l, int r, int x, int v)
{
if(ql <= l && r <= qr)
{
a[x] += v, tg[x] += v;
return;
}
int mid = l + r >> 1; pd(x);
if(mid >= ql) upd(ql, qr, l, mid, x << 1, v);
if(mid < qr) upd(ql, qr, mid + 1, r, x << 1 | 1, v);
pu(x);
}
void build(int l, int r, int x)
{
if(l == r) return c[x] = 1, void();
int mid = l + r >> 1;
build(l, mid, x << 1);
build(mid + 1, r, x << 1 | 1);
pu(x);
// cerr << c[x] << endl;
}
}t;
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);
cin >> n;
for(int i = 1; i <= n; i ++) cin >> a[i];
// ans = n;
t.build(1, n, 1);
for(int i = n; i >= 1; i --)
{
if(!pos[a[i]]) nxt[i] = n + 1;
else nxt[i] = pos[a[i]];
pos[a[i]] = i;
}
for(int i = n; i >= 1; i --)
{
t.upd(i, n, 1, n, 1, 1);
if(nxt[i] != n + 1)
{
if(nxt[nxt[i]] != n + 1)
{
t.upd(nxt[nxt[i]], n, 1, n, 1, 1);
}
t.upd(nxt[i], n, 1, n, 1, -2);
}
// cerr << t.c[1] << endl;
if(t.a[1] == 0) ans += n - t.c[1];
else ans += n;
}
cout << ans;
return 0;
}