【JOISC2022】坏掉的设备 2

首先叉掉几个变动很大的要素,逆序对、连续段就不要想了。考虑前缀和

\(0\) 视为 \(-1\),把 \(1\) 视为 \(+1\)。构造 \(t_i=1010\cdots\),把需要表示的 \(A_i\) 的信息存储在 \(s_i\) 里。

\(s_i,t_i\) 乱序归并后,\(s_i\)前缀和要么不变,要么 \(+1\)。对于一个 \(n\) 位二进制信息 \(a_{1\sim n}\),考虑如何把它放到 \(s_i\) 里,然后还原出来。

乱序归并后,扫描 \(u_i\),设当前已经扫过了 \(len\)\(t_i\) 的元素。在第一个 \(s_i\) 元素前,前缀和 \(S=len\bmod 2\)。如果 \(a\) 的第一个 \(01\) 段是 \(1\) 段,我们把 \(a_j\)\(s_i\)重复两遍,设位置 \(k,k+1\)。解码时当 \(S=2\) 时还原 \(a_j=1\),并令 \(S\gets 0\)。此时 \(a_j\) 要么在 \(k\) 被还原,要么在 \(k+1\) 被还原。扫完这个段后仍然有 \(S=len\bmod 2\)

类似的,如果第一个段是 \(0\) 段,我们先加入一个 \(a_1\),此时 \(S=(len\bmod 2)-1\)。然后再加入两个 \(a_1\),两个 \(a_2\)……一旦 \(S=-2\) 还原位并清零。扫完段后仍有 \(S=(len\bmod 2)-1\)

那么一般地,令 \(a_0=1\),把 \(a_j\) 重复放 \([a_j\ne a_{j-1}]+2\) 次即可。

注意每次 Anna 传输的 \(s_i,t_i\) 长度可以变化。

g[0]=1;
for(int i=1;i<=n;i++){//n=140
    if(i>=2) g[i]+=g[i-2];
    if(i>=3) g[i]+=g[i-3];
}
ll ans=0;
for(int i=1;i<=n;i++) ans+=g[i];
printf("%lld\n",ans);

能表示 210020449144859288 种状态。

继续优化,考虑在 \(s_i\) 不断加一个和末尾不同的数字,也就是一段 \(01\),那么 \(|S|<2\),不会影响前面的还原过程,同时可以得出增加 \(01\) 段的长度信息。

g[0]=1;
for(int i=1;i<=n;i++){//n=140
	if(i>=2) g[i]+=g[i-2];
	if(i>=3) g[i]+=g[i-3];
}
ll ans=0;
for(int i=0;i<=n;i++) ans+=g[i]*(n-i+1);
printf("%lld\n",ans-1);//不能传递长为 0 的串

能表示 856798505175074100 种状态。

不妨把 \(s_i\) 的第一位作为标识位。标识位为 \(1\) 后面不动;否则 \(s_i,t_i\)\(0/1\) 全部反转。

如果标识位为 \(1\)\(u_i\) 的第一位必为 \(1\)\(S\) 初始值 \(-1\);否则 \(u_i\) 的第一位必为 \(0\)\(S\) 初始值 \(1\)。以此区分。

g[0]=1;
for(int i=1;i<n;i++){//n=140
	if(i>=2) g[i]+=g[i-2];
	if(i>=3) g[i]+=g[i-3];
}
ll ans=0;
for(int i=0;i<n;i++) ans+=g[i]*(n-i);
printf("%lld\n",ans*2);

能表示 1293556112060429624 种状态,足够了。

参考代码:https://uoj.ac/submission/790777

posted on 2025-09-13 22:02  xinjingzhu  阅读(9)  评论(0)    收藏  举报