AT4108 [ARC094D] Normalization

AT4108 [ARC094D] Normalization

套路了,设 a a a b b b c c c 分别为 0 0 0 1 1 1 2 2 2,则所有操作得到的数字串的和必然模 3 3 3 同余。

且操作得到的数字串必然有相邻相等的数字(注意特判开始是否有相邻相等的字符)。

根据暴力输出情况发现当 n > 3 n>3 n>3 时上述条件为充分条件。

考虑归纳证明: 如果 n = k n=k n=k 时条件充分,长度为 n = k + 1 n=k+1 n=k+1 的字符串 s s s t t t s s s 一定能够经过若干次操作将其首字母换成 t t t 的首字母,由于 n = k n=k n=k 时满足充分,即 s s s t t t 后缀 [ 2 , k + 1 ] [2,k+1] [2,k+1] 满足充分,此时首字母相等显然也充分,证毕。

故从 s s s (满足 s s s 可操作)可变到 t t t,需充分满足上面两个限制。

故当 n ≤ 3 n\leq 3 n3 时 暴力。

否则根据这两个限制 DP,答案即为满足数位和模 3 3 3 同余初始串且含有相邻相等的数字的数字串个数

考虑容斥,即数位和模 3 3 3 同余初始串的数字串个数 − - 数位和模 3 3 3 同余初始串且含有相邻相等的数字的数字串个数。

对于前面的,答案显然为 3 n − 1 3^{n-1} 3n1,即任意枚举 n − 1 n-1 n1 位,最后一位视情况而定。

对于后面的,DP 状态更少,记 f i , j , k f_{i,j,k} fi,j,k 表示前 i i i 位,数位和为 j j j,当前位为 k k k 的满足条件的数字串个数。

转移方程:枚举上一位取值, f i , j , k = ∑ l = 0 2 f i − 1 , ( j − k )   m o d   3 , l [ l ≠ k ] f_{i,j,k}=\sum\limits_{l=0}^{2}f_{i-1,(j-k)\bmod 3,l}[l \ne k] fi,j,k=l=02fi1,(jk)mod3,l[l=k]

最后,总答案即为 3 n − 1 − ∑ i = 0 2 f n , s , i − f l g 3^{n-1}-\sum\limits_{i=0}^{2}f_{n,s,i}-flg 3n1i=02fn,s,iflg(其中 s s s 表示初始字符串的数位和模 3 3 3 的余数,布尔变量 f l g flg flg 表示初始字符串是否有相邻相等的字符)。

时间复杂度 O ( n ) \mathcal O(n) O(n)

#include<bits/stdc++.h>

using namespace std;

#define int long long
typedef long long ll;

#define ha putchar(' ')
#define he putchar('\n')

inline int read() {
	int x = 0, f = 1;
	char c = getchar();
	while (c < '0' || c > '9') {
		if (c == '-')
			f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')
		x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
	return x * f;
}

inline void write(int x) {
	if (x < 0) {
		putchar('-');
		x = -x;
	}
	if (x > 9)
		write(x / 10);
	putchar(x % 10 + 48);
}

const int _ = 2e5 + 10, mod = 998244353;

int n, S, f[_][3][3], ans;

string s;

int qpow(int x, int y) {
	int res = 1;
	while (y) {
		if (y & 1)res = res * x % mod;
		x = x * x % mod;
		y >>= 1;
	}
	return res;
}

queue<string> q;

map<string, bool> mp;

char js(int x)
{
	x %= 3;
	if(!x) return 'a';
	if(x == 1) return 'c';
	return 'b';
}

void solve()
{
	q.push(s);
	mp[s] = 1;
	while(!q.empty())
	{
		string nw = q.front();
		q.pop();
//		cout << nw << "\n";
		for(int i = 0; i < n - 1; ++i)
			if(nw[i] != nw[i + 1])
		{
			string ss = nw;
			ss[i] = ss[i + 1] = js(nw[i] - 'a' + nw[i + 1] - 'a');
			if(mp.find(ss) == mp.end())
			{
				q.push(ss);
				mp[ss] = 1;
			}
		}
	}
//	for(auto v : mp) cout << v.first << "\n";
	write(mp.size()), he;
}

signed main() {
	cin >> s;
	n = s.size();
	bool flg = 0;
	for (int i = 0; i < n - 1; ++i)
		if (s[i] != s[i + 1]) {
			flg = 1;
			break;
		}
	if (!flg) return puts("1"), 0;
	if(n <= 3) return solve(), 0;
	flg = 1;
	for (int i = 0; i < n - 1; ++i)
		if (s[i] == s[i + 1]) {
			flg = 0;
			break;
		}
	for (int i = 0; i < n; ++i) S = (S + s[i] - 'a') % 3;
	ans = qpow(3, n - 1);
	for (int i = 0; i <= 2; ++i) f[0][i][i] = 1;
	for (int i = 1; i < n; ++i)
		for (int j = 0; j <= 2; ++j)
			for (int k = 0; k <= 2; ++k)
				for (int l = 0; l <= 2; ++l)
					if (l != k)  f[i][j][k] = (f[i][j][k] + f[i - 1][(j - k + 3) % 3][l]) % mod;
	for (int i = 0; i <= 2; ++i) ans = (ans - f[n - 1][S][i] + mod) % mod;
	write(ans + flg), he;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值