【ICPC】2022西安站 E. Find Maximum | 三进制

923 阅读1分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

【ICPC】2022西安站 E. Find Maximum | 三进制

像是传统二进制题目的三进制版本(

题目链接

Problem - E - Codeforces

题目

image.png

题目大意

对一个正整数 xx 定义函数

f(x)={1(x=0)f(x3)+1(x>0(3x))f(x1)+1(x>0(3x))f(x)= \begin{cases} 1&(x=0)\\ f(\frac{x}{3})+1&(x>0\land(3\mid x))\\ f(x-1)+1&(x>0\land(3\nmid x))\\ \end{cases}

给定 llrr,求 maxi=lr(f(x))\max_{i=l}^r(f(x))

多组数据。

思路

看到题第一眼感觉答案最大的一定是 ((23+2)3+2)3+2...((2*3+2)*3+2)*3+2...

然后发现这非常类似于常见的二进制问题,首先我们将 xx 转化为 3 进制形式,容易发现 f(x)f(x) 实际上就是各个数位上的数字加一求和。如 342342 按照上述过程我们可以计算出

f(342)=f(114)+1=f(38)+1+1=f(37)+1+1+1=f(36)+1+1+1+1=f(12)+1+1+1+1+1=f(4)+1+1+1+1+1+1=f(3)+1+1+1+1+1+1+1=f(1)+1+1+1+1+1+1+1+1=f(0)+1+1+1+1+1+1+1+1+1=1+1+1+1+1+1+1+1+1+1=10\begin{aligned} f(342)&=f(114)+1\\ &=f(38)+1+1\\ &=f(37)+1+1+1\\ &=f(36)+1+1+1+1\\ &=f(12)+1+1+1+1+1\\ &=f(4)+1+1+1+1+1+1\\ &=f(3)+1+1+1+1+1+1+1\\ &=f(1)+1+1+1+1+1+1+1+1\\ &=f(0)+1+1+1+1+1+1+1+1+1\\ &=1+1+1+1+1+1+1+1+1+1\\ &=10 \end{aligned}

实际上在三进制的视角下,就是把末位减到 11,再扔掉 00,直到把整个数变成 00342342 的三进制形式为 (110200)3(110200)_{3}, 则

f(342)=(1+1)+(1+1)+(0+1)+(2+1)+(0+1)+(0+1)=2+2+1+3+1+1=10\begin{aligned} f(342)&=(1+1)+(1+1)+(0+1)+(2+1)+(0+1)+(0+1)\\ &=2+2+1+3+1+1\\ &=10 \end{aligned}

所以我们可以用处理二进制下该类问题的方式,类似地处理三进制问题。

最初 x=0x=0,自高向低处理到第 ii 位时,试图让 xx 的当前位为 ri1r_i-1,后面的数位全部填上 22。若 xx 这样填充后比 ll 大,就可以用 xx 来更新答案。然后我们把 xx 的第 ii 位填上 rir_i 继续进行上述过程。

代码

#include <bits/stdc++.h>

using namespace std;
using LL=long long;
int T,l[41],r[41];
LL L,R,x,bas[41],sum,ans;
const int N=38;
int main()
{
	bas[0]=1;
	for (int i=1;i<=N;++i) bas[i]=bas[i-1]*3;
	for (scanf("%d",&T);T--;)
	{
		x=ans=sum=0;
		scanf("%lld%lld",&L,&R);
		for (int i=N-1,flag=0;i>=0;--i)
		{
			l[i]=L%bas[i+1]/bas[i];
			r[i]=R%bas[i+1]/bas[i];
			if (r[i]!=0) flag=1;
			if (!flag) continue;
			if (r[i]!=0&&(x+r[i]*bas[i]-1)>=L)
			{
				if (r[i]==1&&sum==0) ans=max(sum+i*3ll,ans);
				else ans=max(sum+r[i]+i*3ll,ans);
			}
			sum+=r[i]+1;
			x+=r[i]*bas[i];
		}
		printf("%lld\n",max(ans,sum));
	}
        return 0;
		
}