ABC346题解(F&G)
令 \(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;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步