ARC184B 123 Set

题解

Posted by WrongAnswer_90's Blog on October 7, 2024

My Blogs

[ARC184B] 123 Set

首先一个最基本的性质是如果 $x$ 不是 $2$ 或者 $3$ 的倍数则 $x$ 一定会被操作。基于这个简单观察,可以进一步的发现:取出所有不是 $2,3$ 的倍数的数 $x$,则对于不同的 $x$,$2^i3^jx$ 之间是没有关系的。所以可以对于不同的 $x$ 分别处理。

考虑操作了 $x$ 之后可以覆盖掉 $2x,3x$,很深刻的想法是看成一个矩形,左上角是 $x$,每向右一格 $\times 3$,每向下一格 $\times 2$。显然对于一个 $x$ 如果只保留 $\leq n$ 的表中的数,则其随着行数的增加,列数在减小。这是一个杨表,而操作一个数相当于把格子 $(i,j),(i,j+1),(i+1,j)$ 覆盖,而要求的就是用尽量少的操作数把这个杨表全都覆盖。

因为列数不会很多(最多 $19$ 列,$3^{19}>10^9$),所以可以直接轮廓线 DP,状压记录轮廓线上的每个格子是否被操作了,转移也比较简单。

如何计算最终答案?考虑计算每个杨表对应了多少个 $x$。把杨表中的数从小到大加入,设其中最大的数是 $v_i$,则其对应了 $\lfloor\frac n{v_i}\rfloor-\lfloor \frac n{v_{i+1}}\rfloor$。而不同杨表的个数显然不会太多,对每个暴力求就能过,当然也可以进行打表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
int n;
int f[2][20][1<<19];
int a[31],pw2[40],pw3[40];
inline int solve()
{
	for(int i=0;i<2;++i)for(int j=0;j<=a[1];++j)
	for(int k=0;k<(1<<a[1]);++k)f[i][j][k]=inf;
//		for(int i=1;i<=30;++i)write(a[i]);
//		puts("");
	int now=1,pre=0;
	f[0][0][0]=0;
	for(int i=1;i<=31;++i)
	{
		swap(now,pre);
		if(!a[i])return f[now][0][0];
		for(int j=0;j<a[i];++j)
		{
			for(int k=0;k<(1<<a[i]);++k)
			{
				int lef=j?((k>>(j-1))&1):0,up=(k>>j)&1;
				if(lef|up)Mmin(f[now][j+1][k&(~(1<<j))],f[now][j][k]);
				Mmin(f[now][j+1][k|(1<<j)],f[now][j][k]+1);
			}
		}
		for(int j=0;j<=a[i+1];++j)
		{
			for(int k=0;k<(1<<a[i+1]);++k)
			f[pre][j][k]=inf;
		}
		int lim=(1<<a[i+1])-1;
		for(int k=0;k<(1<<a[i]);++k)
		Mmin(f[pre][0][k&lim],f[now][a[i]][k]);
	}
	assert(0);
	return 0;
}
inline int get(int x){return x-x/2-x/3+x/6;}
int F[500]={
	//...
};
inline void mian()
{
	vt ve;
	read(n);
	pw2[1]=pw3[1]=1;
	for(int i=2;i<=34;++i)pw2[i]=pw2[i-1]*2;
	for(int i=2;i<=22;++i)pw3[i]=pw3[i-1]*3;
	for(int i=1;pw2[i]<=n;++i)
	{
		for(int j=1;pw2[i]*pw3[j]<=n;++j)
		ve.eb(tup(pw2[i]*pw3[j],i,j));
	}
//		exit(0);
	sort(ve.begin(),ve.end(),[&](tup x,tup y){return x.x<y.x;});
	ve.eb(tup(n+1,0,0));
	int ans=0;
	for(int i=0;i<(int)ve.size()-1;++i)
	{
		int tmp=get(n/ve[i].x)-get(n/ve[i+1].x);
		a[ve[i].y]=ve[i].z;
		//F[i]=solve()
		ans+=tmp*solve();
	}
	write(ans);
}