CF1423J - Bubble Cup hypothesis 题解

本来是想刷水题过日子来着,就随便开了个 2400,结果发现是个神仙题/kk


Portal

首先注意到,当 \(2^{0\sim i}\) 的系数都填上 \(7\) 时,这时候的结果等于 \(7\sum\limits_{j=0}^i2^j=7\left(2^{i+1}-1\right)\),最小的严格覆盖它的 \(2\) 的整次幂是 \(2^{i+4}\),也就是最多会超出 \(2^i\) 三位。于是我们可以从低位往高位 DP,任意时刻需要满足前 \(i\) 位都符合了,因为以后再也不可能影响到它们了,然后需要记录后面三位是多少。转移的时候就直接枚举状态,减一下,看当前系数是否在 \([0,8)\) 内,然后加上去。于是我们得到了一个 \(\mathrm O\!\left(T\cdot 60\times 8^2\right)\) 的算法,算一下是 1e9,但是只有加法,写个快模常数会非常小,于是考虑莽一波。

for(int i=1;i<=60;i++)for(int j=0;j<8;j++){
	int shd=(j<<1)+((x>>i-1)&1);
	dp[i][j]=0;
	for(int k=0;k<8;k++)
		0<=shd-k&&shd-k<8&&add(dp[i][j],dp[i-1][k]);
}

显然会 T,\(n=10^5\) 的时候已经 \(889\mathrm{ms}\) 了。接下来考虑施展卡常大法/cy

首先手动 O3,fread / fwrite 快读快写走起。然后发现 #define int long long 会严重降低效率,于是只把读入设成 ll 其他都用 int。然后将 for(k) 这层循环展开,注意到 dp[i][j] 多次使用,于是取个引用避免多次计算地址。

for(int i=1;i<=60;i++)for(int j=0;j<8;j++){
	const int shd=(j<<1)+((x>>i-1)&1);int &d=dp[i][j]=0;
	0<=shd&&shd<8&&(d+=dp[i-1][0])>=mod&&(d-=mod),
	1<=shd&&shd<9&&(d+=dp[i-1][1])>=mod&&(d-=mod),
	2<=shd&&shd<10&&(d+=dp[i-1][2])>=mod&&(d-=mod),
	3<=shd&&shd<11&&(d+=dp[i-1][3])>=mod&&(d-=mod),
	4<=shd&&shd<12&&(d+=dp[i-1][4])>=mod&&(d-=mod),
	5<=shd&&shd<13&&(d+=dp[i-1][5])>=mod&&(d-=mod),
	6<=shd&&shd<14&&(d+=dp[i-1][6])>=mod&&(d-=mod),
	7<=shd&&shd<15&&(d+=dp[i-1][7])>=mod&&(d-=mod);
}

目前 \(n=10^5\) 已经降低到 \(404\mathrm{ms}\),继续卡。我们考虑将 for(j) 这层也展开。这下好了,发现新大陆了。发现对于每次转移,能够转移的条件是关于 \(j=0\) 时的 \(shd\) 的一个不等式,而它是个 \(0/1\),不像 \(j>0\) 的时候还要加上 j<<1。然后还有,全部展开的话,会发现对于某些 \(j,k\) 根本无论如何也不可能转移,这样可以直接手动删除。于是这样展开后,转移分为三类:仅 \(shd=0\) 时,仅 \(shd=1\) 时,所有时。总共的转移量只有 \(36\),比原来的 \(8^2=64\) 要少了一半。顺便再把 \(dp_{i,0\sim 7}\) 一起取个引用,发挥通常情况下循环展开的作用。

for(int i=1;i<=60;i++){
	const bool shd=(x>>i-1)&1;
	int &d0=dp[i][0],&d1=dp[i][1],&d2=dp[i][2],&d3=dp[i][3],&d4=dp[i][4],&d5=dp[i][5],&d6=dp[i][6],&d7=dp[i][7];
	#define add(x,y) (d##x+=dp[i-1][y])>=mod&&(d##x-=mod)
	shd||(add(4,1),add(5,3),add(6,5),add(7,7)),
	add(3,0),add(4,2),add(5,4),add(6,6),
	add(3,1),add(4,3),add(5,5),add(6,7),
	add(2,0),add(3,2),add(4,4),add(5,6),
	add(2,1),add(3,3),add(4,5),add(5,7),
	add(1,0),add(2,2),add(3,4),add(4,6),
	add(1,1),add(2,3),add(3,5),add(4,7),
	add(0,0),add(1,2),add(2,4),add(3,6),
	shd&&(add(0,1),add(1,3),add(2,5),add(3,7));
}

此时 \(n=10^5\) 需要跑 \(280\mathrm{ms}\),算一下 \(n=5\times 10^5\) 的时候是 \(1.4\mathrm s\),快成功了。进一步,根据一堆寻址,如果按照地址连续寻址会更快这个理论,我们把中间一堆所有时的转移按照第一维排个序,快了大概 \(0.1\mathrm s\)。然后注意到 \(dp_{i,0\sim 7}\) 多次使用,虽然已经取引用了,但还有更快的访问方法:存到寄存器里(只有 \(8\) 个,寄存器装得下)。注意 register int &d0=dp[i][0]; 是卵用没有的,因为它到底还是取了引用,那么就不可能把这个变量从原来处于的地方给重新装到寄存器里。于是必须重新定义变量,在一开始定义的时候就装到寄存器里,算完之后再赋到 dp 数组里。

for(int i=1;i<=60;i++){
	bool shd=(x>>i-1)&1;
	register int d0=0,d1=0,d2=0,d3=0,d4=0,d5=0,d6=0,d7=0;
	#define add(x,y) (d##x+=dp[i-1][y])>=mod&&(d##x-=mod)
	add(0,0),add(1,0),add(1,1),add(1,2),add(2,0),add(2,1),add(2,2),add(2,3),add(2,4),add(3,0),add(3,1),add(3,2),add(3,3),add(3,4),add(3,5),add(3,6),add(4,2),add(4,3),add(4,4),add(4,5),add(4,6),add(4,7),add(5,4),add(5,5),add(5,6),add(5,7),add(6,6),add(6,7),
	shd||(add(4,1),add(5,3),add(6,5),add(7,7)),shd&&(add(0,1),add(1,3),add(2,5),add(3,7));
	dp[i][0]=d0,dp[i][1]=d1,dp[i][2]=d2,dp[i][3]=d3,dp[i][4]=d4,dp[i][5]=d5,dp[i][6]=d6,dp[i][7]=d7;
}

这个寄存器的效果是真的惊人,直接给我最大点跑了 \(608\mathrm{ms}\)。至此这道题被我野蛮的 AC 了。所以这道题是一个练习卡常的好题(×


显然上面那个不是正解,而是我自己瞎 yy 的,所以接下来讲正解。

考虑将一组系数 \(x_{0\sim n}\) 满足条件的式子写出来:

\[\sum\limits_{i=0}^n2^ix_i=m \]

注意到 \(x_i\in[0,8)\),想到八进制,考虑将这么多项三个分一组:

\[\sum\limits_{i=0}^{\frac n3}8^i(x_{3i}+2x_{3i+1}+4x_{3i+2})=m\\\sum\limits_{i=0}^{\frac n3}8^ix_{3i}+2\sum_{i=0}^{\frac n3}8^ix_{3i+1}+4\sum_{i=0}^{\frac n3}8^ix_{3i+2}=m \]

这时候注意到,这三项等价于八进制数,也就是值与 \(x\) 一一对应,于是我们只要计算关于正整数 \(a,b,c\) 的方程 \(a+2b+4c=m\) 的解的组数即可。这个就非常简单了:

\[\begin{aligned}ans&=\sum_{i=0}^{\left\lfloor\frac m4\right\rfloor}\sum_{j=0}^{\left\lfloor\frac{m-4i}2\right\rfloor}1\\&=\sum_{i=0}^{\left\lfloor\frac m4\right\rfloor}\left(\left\lfloor\dfrac{m-4i}2\right\rfloor+1\right)\\&=\sum_{i=0}^{\left\lfloor\frac m4\right\rfloor}\left(\left\lfloor\dfrac m2\right\rfloor+1-2i\right)\\&=\left(\left\lfloor\dfrac m4\right\rfloor+1\right)\left(\left\lfloor\dfrac m2\right\rfloor+1\right)-\left\lfloor\dfrac m4\right\rfloor\left(\left\lfloor\dfrac m4\right\rfloor+1\right)\end{aligned} \]

posted @ 2020-11-21 17:43  ycx060617  阅读(134)  评论(2编辑  收藏  举报