ARC 157 F Sol

嫌弃讲题人的我,准备好好写一篇题解。

link to problem

观察数据范围:\(1\le N\le 50\)

如果是 \(20\),想到 \(2^{20}\);如果是 \(40\),想到 \(2^{40\div 2}\);若果是 \(50\) 呢?\(2^{25}\) 有点大,于是想到 \(2^{50\div 3}\)。但是如何去凑?

Hint
\(N\) \(S\) \(T\) \(res\)
\(6\) \(\tt{XXXXXX}\) \(\tt{YYYYYY}\) \(\tt{XYXYX}\)
\(8\) \(\tt{XXXXXXXX}\) \(\tt{XXXXXXXX}\) \(\tt{XXXXXXXX}\)
\(20\) \(\tt{YYYXYYXXXXXYYXYYXXXY}\) \(\tt{YXYYXXXYYXYYYYYYXXYY}\) \(\tt{YYYXYXXYXXYYYYYXXY}\)

备注:第一行是最坏情况,第二行最好,第三行随机。

What you can see

答案长度都很大?再看看。最小长度多少?

lemma: 答案 \(\ge \lfloor \frac{2N}{3}\rfloor\)

证明:\(N=3k+r\)证明长度是 \(3\) 的答案 \(\ge 2\)\(r\) 的部分。

  • 长度为 \(1\)。最坏 \(S=\tt{X},T=\tt{Y}\),答案是 \(0\)

  • 长度为 \(2\)。若有一个相同,答案 \(\ge 1\);若都不相同即 \(S=\tt{XX},T=\tt{YY}\),可交换使答案为 \(2\)

  • 长度为 \(3\)。第一个和第三个若有一个相同,转化为 长度为 \(2\) 的加上 \(1\),答案显然 \(\ge 2\);都不相同,若第二个相同(\(S=\tt{XXX},T=\tt{YXY}\))或不相同(\(S=\tt{XXX},T=\tt{YYY}\)),均可答案 \(\ge 2\)

考虑 \(r\) 是几。

  • \(r=0\) 显然 答案 \(\ge \lfloor \frac{2N}{3}\rfloor\)

  • \(r=1\)\(1\times 2\) 对除 \(3\) 下取整没有影响。

  • \(r=2\)\(2\times 2\) 贡献一个 \(1\),正好。

即证。

lemma \(2\): 如果将两个数匹配,若设第一个是 \(S\) 的第 \(i\) 位,第二个 \(T\) 的第 \(j\) 位,那么 \(\mid i-j \mid\le \frac{N}{3}\)

证明:若不满足,上面的 lemma 就不满足了。

我们得到了一个 \(\frac{N}{3}\)。看来,\(2^{50\div 3}\) 凑好了?

(Small) Hint $2$

考虑状压 dp?

此后令 \(l=(N+2)/3+1\)。设计一个 dp 状态。设 \(dp_{i,msk}\) 代表 \(S,T\) 匹配到第 \(i\) 位了,现在没有用上的是 \(msk\)

在转移之前,先弄清如何将一个 \(\tt{XY}\) 串 encode 成 \(01\) 串的 \(msk\)。考虑到:

  • 答案要求在长度最长的基础下,字典序最小的。

  • \(01\) 串长度得记录,否则会出现不同字符串对应相同的 \(01\) 串。

现在有两个选择:

  • \(\texttt{X}=0,\texttt{Y}=1\),dp 时求 min。

  • \(\texttt{X}=1,\texttt{Y}=0\),dp 时求 max。

显然第二种更好处理长度的记录。为了让更长的长度更优,在最前面的一位放上一个 \(1\)

一些栗子
字符串 对应的 \(msk\)
\(\tt{XXX}\) \(1111\)
\(\tt{YY}\) \(100\)
\(\tt{YX}\) \(101\)

lemma \(3\): 如果你要用 \(msk\) 里面的东西来匹配 \(i+1\),那么越往前的越优。

证明:显然的贪心。

这样我们来预处理一下对于每一个 \(msk\),现在要匹配一个 \(a\),加上一个 \(b\),会怎样。

具体实现

\(to_{msk,a,b}\) 是预处理的答案。

  • \(a=0\)。找到除了第一位第一个 \(0\),把前面的全删除,左移 \(1\),或上 \(b\)

  • \(a=1\)。找到除了第一位第一个 \(1\),把前面的全删除,左移 \(1\),或上 \(b\)

code
void init(){
	// to_{msk,a,b}: you have msk, you want to get one a, you add b.
	for (ll msk=1; msk<(1<<l); msk++){
		ll len=0;
		while ((msk^(1<<len))>(1<<len)){
			len++;
		}
		len--;
		ll cur=len;
		while (~cur && (msk&(1ll<<cur))>0){
			cur--;
		}
		if (~cur){
			to[msk][0][0]=((msk&((1ll<<cur)-1))^(1ll<<cur));
			to[msk][0][0]<<=1ll;
			to[msk][0][1]=to[msk][0][0]^1;
		}
		cur=len;
		while (~cur && (msk&(1ll<<cur))==0){
			cur--;
		}
		if (~cur){
			to[msk][1][0]=((msk&((1ll<<cur)-1))^(1ll<<cur));
			to[msk][1][0]<<=1ll;
			to[msk][1][1]=to[msk][1][0]^1;
		}
	}
}

万事具备,只欠东风!考虑 dp 怎么转移。

分三种情况:

  • \(S_i=\texttt{X},T_i=\texttt{X}\)

  • \(S_i=\texttt{Y},T_i=\texttt{Y}\)

  • \(S_i=\texttt{X},T_i=\texttt{Y}\)\(S_i=\texttt{Y},T_i=\texttt{X}\)

情况 \(1,2\)

  • 可以这两个匹配。

  • 可以找前面 \(msk\) 里面的匹配。

  • 可以不匹配。

情况 \(3\)

  • 匹配 \(\tt{X}\)\(\tt{Y}\) 加入 \(msk\)

  • 匹配 \(\tt{Y}\)\(\tt{X}\) 加入 \(msk\)

  • 可以不匹配。

初始:\(dp_{0,1}=1\)

code
dp[0][1]=1;
for (int i=0; i<n; i++){
	for (int msk=0; msk<(1ll<<l); msk++){
		if (dp[i][msk]==0){
			continue;
		}
		if (s[i]=='X' && t[i]=='X'){
			// use this
			dp[i+1][1]=max(dp[i+1][1],dp[i][msk]<<1ll|1);
			if (to[msk][1][1]!=0){
				// use before
				dp[i+1][to[msk][1][1]]=max(dp[i+1][to[msk][1][1]],dp[i][msk]<<1ll|1);
			}
			if (msk<(1<<l-1)){
				// no use
				dp[i+1][msk<<1ll|1]=max(dp[i+1][msk<<1ll|1],dp[i][msk]);
			}
		}
		else if (s[i]=='Y' && t[i]=='Y'){
			// use this
			dp[i+1][1]=max(dp[i+1][1],dp[i][msk]<<1ll);
			if (to[msk][0][0]!=0){
				// use before
				dp[i+1][to[msk][0][0]]=max(dp[i+1][to[msk][0][0]],dp[i][msk]<<1ll);
			}
			if (msk<(1<<l-1)){
				// no use
				dp[i+1][msk<<1ll]=max(dp[i+1][msk<<1ll],dp[i][msk]);
			}
		}
		else{
			// use x
			if (to[msk][1][0]!=0){
				dp[i+1][to[msk][1][0]]=max(dp[i+1][to[msk][1][0]],dp[i][msk]<<1ll|1);
			}
			// use y
			if (to[msk][0][1]!=0){
				dp[i+1][to[msk][0][1]]=max(dp[i+1][to[msk][0][1]],dp[i][msk]<<1ll);
			}
			// no use
			if (msk<(1<<l-1)){
				dp[i+1][msk<<1ll]=max(dp[i+1][msk<<1ll],dp[i][msk]);
				dp[i+1][msk<<1ll|1]=max(dp[i+1][msk<<1ll|1],dp[i][msk]);
			}
		}
	}
}

输出答案 \(\max_{i=1}^{l-1} dp_{N,i}\) 即可。

完整 code
#include <bits/stdc++.h>

using namespace std;

#define de(x) cout<<#x<<"="<<x<<endl

using ll = long long;

const int N = 51;
const int M = 1<<18;

ll n,l;
string s,t;
ll to[M][2][2];
ll dp[N][M];

void init(){
	// to_{msk,a,b}: you have msk, you want to get one a, you add b.
	for (ll msk=1; msk<(1<<l); msk++){
		ll len=0;
		while ((msk^(1<<len))>(1<<len)){
			len++;
		}
		len--;
		ll cur=len;
		while (~cur && (msk&(1ll<<cur))>0){
			cur--;
		}
		if (~cur){
			to[msk][0][0]=((msk&((1ll<<cur)-1))^(1ll<<cur));
			to[msk][0][0]<<=1ll;
			to[msk][0][1]=to[msk][0][0]^1;
		}
		cur=len;
		while (~cur && (msk&(1ll<<cur))==0){
			cur--;
		}
		if (~cur){
			to[msk][1][0]=((msk&((1ll<<cur)-1))^(1ll<<cur));
			to[msk][1][0]<<=1ll;
			to[msk][1][1]=to[msk][1][0]^1;
		}
	}
}

void print(ll x){
	ll len=0;
	while ((x^(1ll<<len))>(1ll<<len)){
		len++;
	}
	len--;
	for (ll i=len; i>=0; i--){
		int t=(x>>i)&1;
		if (t){
			cout<<"X";
		}
		else{
			cout<<"Y";
		}
	}
	cout<<endl;
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);

	cin>>n>>s>>t;
	l=(n+2)/3+1;
	init();
	// push
	dp[0][1]=1;
	for (int i=0; i<n; i++){
		for (int msk=0; msk<(1ll<<l); msk++){
			if (dp[i][msk]==0){
				continue;
			}
			if (s[i]=='X' && t[i]=='X'){
				// use this
				dp[i+1][1]=max(dp[i+1][1],dp[i][msk]<<1ll|1);
				if (to[msk][1][1]!=0){
					// use before
					dp[i+1][to[msk][1][1]]=max(dp[i+1][to[msk][1][1]],dp[i][msk]<<1ll|1);
				}
				if (msk<(1<<l-1)){
					// no use
					dp[i+1][msk<<1ll|1]=max(dp[i+1][msk<<1ll|1],dp[i][msk]);
				}
			}
			else if (s[i]=='Y' && t[i]=='Y'){
				// use this
				dp[i+1][1]=max(dp[i+1][1],dp[i][msk]<<1ll);
				if (to[msk][0][0]!=0){
					// use before
					dp[i+1][to[msk][0][0]]=max(dp[i+1][to[msk][0][0]],dp[i][msk]<<1ll);
				}
				if (msk<(1<<l-1)){
					// no use
					dp[i+1][msk<<1ll]=max(dp[i+1][msk<<1ll],dp[i][msk]);
				}
			}
			else{
				// use x
				if (to[msk][1][0]!=0){
					dp[i+1][to[msk][1][0]]=max(dp[i+1][to[msk][1][0]],dp[i][msk]<<1ll|1);
				}
				// use y
				if (to[msk][0][1]!=0){
					dp[i+1][to[msk][0][1]]=max(dp[i+1][to[msk][0][1]],dp[i][msk]<<1ll);
				}
				// no use
				if (msk<(1<<l-1)){
					dp[i+1][msk<<1ll]=max(dp[i+1][msk<<1ll],dp[i][msk]);
					dp[i+1][msk<<1ll|1]=max(dp[i+1][msk<<1ll|1],dp[i][msk]);
				}
			}
		}
	}
	ll mx=0;
	for (int msk=1; msk<(1ll<<l); msk++){
		mx=max(mx,dp[n][msk]);
	}
	print(mx);
	return 0;
}

submission(有一些调试用的注释)

posted @ 2023-08-16 22:10  SFlyer  阅读(46)  评论(1编辑  收藏  举报