[日常摸鱼]算是一些思维题-CF680D/Hamming Code
https://codeforces.com/contest/680/problem/D
你需要确定一个\(X(X\leq m)\),\(m\leq 10^{15}\),每次操作会找一个最大的不超过\(X\)的\(a^3\),然后把\(X\)变成\(X-a^3\),直到为0,问最多能操作多少次,以及在操作次数最多的情况下\(X\)最大是多少?
考虑对于一个规模为\(m\)的问题(我们同样是自己选\(X\leq m\)),考虑怎么转换成一个规模更小的问题:
对于当前的\(X\),每次会选出最大的是\(a:a^3\leq X,(a+1)^3>X\),后面那个也就是\(X\leq (a+1)^3-1\)。
-
(1)如果接下来的操作直接挖掉一个\(a^3\),那我们就可以直接取\(X=m\),问题转化成规模为\(m'=m-a^3\)的问题。
-
(2)如果接下来的操作挖掉大小的是\((a-1)^3\),那就会有约束\((a-1)^3\leq X\),和\(a^3>X\),也就是\(X\leq a^3-1\),注意到这么一件事情:规模越大的问题可以转移到的子问题的集合也会越大(因为只要\(X\leq m\)就好了),所以我们贪心地选取\(X=a^3-1\),问题转化成\(m'_1=a^3-1-(a-1)^3=3a^2-3a=3a(a-1)\geq 0\)。
-
(3)以此类推,对于这一次操作挖掉\((a-t)^3\)的情况,问题会转化成\(m_t'=(a-t+1)^3-1-(a-t)^3\)
对于(2)和(3)我们似乎可以稍微比较一下哪个决策会更优:
\(\begin{aligned}m'_1-m'_t&=a^3-(a-t+1)^3-[(a-1)^3-(a-t)^3]\\&=(t-1)[a^2+a(a-t+1)+(a-t+1)^2]-(t-1)[(a-1)^2+(a-1)(a-t)+(a-t)^2]\\&=2(t-1)(2a-t)\end{aligned}\)
对于\(t\geq 2\),\(t-1> 0\),同时我们上面讨论的是挖掉一个\((a-t)^3>0\)的情况,就会有\(2a-t>0\),所以\(m_1'>m_t'\),(2)对应的情况会比(3)的情况来得优,所以只要考虑(1)和(2)的情况就行啦~
void dfs(ll m,int block,ll X){
if(m==0){
if(block>=r1){
if(block==r1)r2=max(r2,X);
else r2=X;
r1=block;
}
return;
}
ll a=1;
while(calc(a+1)<=m)a++;
dfs(m-calc(a),block+1,X+calc(a));
dfs(calc(a)-1-calc(a-1),block+1,X+calc(a-1));
}
复杂度是O(能过)。
考虑怎么分析这东西的复杂度,对于(1)的决策,我们的\(a\)满足:\(a^3\leq m\leq (a+1)^3-1\),一次操作后变成\(m-a^3\),上界是\((a+1)^3-a^3-1=3a^2+3a\),这似乎说明一个\(O(a^3)\)规模的问题会变成\(O(a^2)\)规模的问题,也就是一个\(O(m)\)级别的问题会变\(O(m^{2/3})\)级别的问题,每次在次数上乘个\(\frac{2}{3}\),多少次会达到\(m=1\)?也就是\(m^{(\frac{2}{3})^t}=1\),最后\(t\)是\(O(\log \log m)\)级别的,确实是O(能过)(雾)。同时对于(2)的决策也是一样的情况,于是整个算法都是O(能过)的啦(
$ $
另一个是之前比赛的一场,翻以前笔记的时候刚好翻到,就也连着发出来。
https://codeforces.com/group/TBxCTUW7hQ/contest/309674/problem/H
Hamming Code,长度为\(2^k\)的二进制码,从0开始标号,纠错码是所有2的幂次的位置和位置0,纠错码\(d(2^j)=\oplus_{i\and2^{j}\neq 0,i\neq 2^j}d(i)\)(也就是\(2^j\)的贡献来自所有二进制表示下\(2^j\)有数字的位置,\(d(0)\)是所有其他位的异或和。保证错误不超过2个,一个汉明码\(k\leq 16\),判断是否有错,有的话有几个地方错,如果只有一个地方错了,指出哪里错
Hamming Code真是神奇…
注意0是所有其他位置的异或和,首先对非纠错码位置,可以\(O(n\log n)\)地求出对应纠错码的值,如果发现有地方不一样,判断\(d(0)\)是否相等,如果相等的话直接说明有两个地方错了,否则能确定只有一个地方有错,再对所有纠错码考虑贡献:
int res=0;
rep(i,0,k-1)if(s[1<<i]!=t[1<<i])res+=(1<<i);
一个类似的问题是小鼠试毒。
小鼠试毒:有\(n\)瓶水,只有其中一个有毒,小鼠一旦喝到有毒的24小时后会死,问至少要几只小鼠才能在24小时后一次找到这瓶毒药。
仿照Hamming Code,先对瓶子标号然后二进制拆分,设\(x=(a_ka_{k-1}\dots a_0)_2\),第一只小鼠喝下所有\(a_0=1\)的药水,第二只喝下所有\(a_1=1\)的…以此类推,一共\(k+1\)只小鼠就能确定位置。同时换成其他进制就没法这么表示了,于是这也是最小次数。