CF切题记
因为@Lcyanstars不来VJ(强烈谴责地),所以要自己找题做!
CF1685B. Linguistics
一个只包含A和B的字符串,判断它能不能被\(a\)个A和\(b\)个B和\(c\)个AB和\(d\)个BA组成?
首先想到这道题dp根本做不了,于是我们考虑贪心()
先把字母个数与字符串不匹配的case排除。
最基础也是最关键的:AB和BA的数量越多越好。
我们考虑把一个字符串的不同块染色。
观察字符串BAABBABBAA,不难发现保证\(c\)和\(d\)的数量最多的情况下,能使AB和BA的数量不同的字串只有一个BAB。
其余的BAAB和BAA都具有惟一性,而BAA中最后一个A只能用单个A来组成。
于是,我们把只能用单个字符组成的字符染色成\(-1\),单个BA和AB染色成\(0\),长度不小于2的相邻字符不同的字串染色成不同正数。
只有长度不小于2的相邻字符不同的字串才能对\(c\)和\(d\)产生影响,我们着重考虑它们(此时不需考虑\(a\),\(b\)):
如果子串长度为奇数,如BABAB,则AB的个数\(c\)和BA的个数\(d\)在满足\(c+d=(len-1)/2\)的情况下可以任意取到。
如果子串长度为偶数,如ABABAB,则可以用\(len/2\)个AB(BA),或AB的个数\(c\)和BA的个数\(d\)在满足\(c+d=len/2-1\)的任意情况下组成。
然后就大功告成了(雾)。
先把染色成\(-1\)和\(0\)和字串依次处理了,由于字符是匹配的,所以我们只用在这一步考虑\(a\)和\(b\)。如果它们用完了(悲),肯定就是不行的了。后面情况\(c\)和\(d\)就算减成负数也没有问题,可以减少一些判断。
最麻烦的还是长度不小于2的相邻字符不同的子串。因为现在无法组成的情况只可能是\(c\)或\(d\)多了,所以我们要尽可能使利用率达到最高。长度为奇数的子串直接统计能放多少个AB BA(因为利用率都一样)。长度为偶数的子串利用率最高的明显是用\(len/2\)个AB(BA)组成,因此优先考虑它。而用\(len/2-1\)个AB和BA组成要损耗1个AB(BA),所以尽可能用长度更小的子串来填充\(c\)和\(d\),最小化AB(BA)的损耗。
int a, b, c, d; string str;
cin>>a>>b>>c>>d>>str;
int n = str.size();
cnt['A'] = cnt['B'] = 0;
rep(i, 0, n) cnt[str[i]]++, color[i] = -1;
if (cnt['A'] != a+c+d) { cout<<"NO"<<endl; continue; }
int p = 0, co = 0;
while(p < n)
{
if (p < n-1 && str[p] != str[p+1])
{
if (p == n-2 || str[p+1] == str[p+2]) color[p] = color[p+1] = 0, p += 2;
else
{
co++;
while(p < n-1 && str[p] != str[p+1]) color[p] = color[p+1] = co, p++;
}
continue;
}
p++;
}
rep(i, 0, n)
{
if (color[i] == -1) if (str[i] == 'A') a--; else b--;
else if (color[i] == 0)
{
if (str[i] == 'A') { if (c) c--; }
else if (d) d--;
i++;
}
}
p = 0; int sum = 0;
vector<pii> v;
while(p < n)
{
if (color[p] > 0)
{
int q = p;
while(p < n-1 && color[p] == color[p+1]) p++;
int len = p-q+1;
if (len%2) sum += len/2;
else
{
if (str[p] == 'B') v.pb({len/2, len/2-1});
else v.pb({len/2-1, len/2});
}
}
p++;
}
sort(v.begin(), v.end(), [](pii a, pii b)
{
return a.first+a.second < b.first+b.second;
});
rep(i, 0, v.size())
{
if (v[i].first > v[i].second && c >= v[i].first) c -= v[i].first, v[i] = {0, 0};
else if (v[i].first < v[i].second && d >= v[i].second) d -= v[i].second, v[i] = {0, 0};
}
rep(i, 0, v.size()) if (v[i].first)
{
int m = min(v[i].first, v[i].second);
if (c >= m) c -= m;
else d -= m-c, c = 0;
}
if (a < 0 || b < 0 || c+d > sum) cout<<"NO"<<endl;
else cout<<"YES"<<endl;