【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
种状态,足够了。