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;
posted @ 2022-07-08 22:02  Loxilante  阅读(66)  评论(2编辑  收藏  举报