【数学 进制 数位DP】P9362 [ICPC 2022 Xi‘an R] Find Maximum|普及+
本文涉及知识点
C++动态规划
数学
P9362 [ICPC 2022 Xi’an R] Find Maximum
题目描述
定义在所有非负整数 xxx 上的函数 f(x)f(x)f(x) 如下:
f(x)={1(x=0)f(x3)+1(x>0∧xmod3=0)f(x−1)+1(x>0∧xmod3≠0)f(x) = \begin{cases} 1 & (x = 0) \\ f(\frac{x}{3}) + 1 & (x > 0\land x\bmod3 = 0) \\ f(x - 1) + 1 & (x > 0\land x\bmod 3\neq 0) \end{cases} f(x)=⎩⎨⎧1f(3x)+1f(x−1)+1(x=0)(x>0∧xmod3=0)(x>0∧xmod3=0)
计算 maxx=lrf(x)\max_{x = l} ^ r f(x)maxx=lrf(x)。
共有 TTT 组数据。
1≤T≤1041\leq T\leq 10 ^ 41≤T≤104,1≤l≤r≤10181\leq l\leq r\leq 10 ^ {18}1≤l≤r≤1018。
输入格式
第一行一个整数 TTT。
接下来 TTT 行,每行两个整数 l,rl, rl,r 表示一组询问。
输出格式
对于每组询问,输出一行一个整数表示答案。
输入输出样例 #1
输入 #1
10
1 2
1 3
1 4
1 5
2 3
2 4
2 5
3 4
3 5
4 5
输出 #1
3
3
4
5
3
4
5
4
5
5
[ICPC 2022 Xi’an R] Find Maximum 数学 进制
x是3倍数,则f(x)=f(x/3)+1,简称操作二。x∈[0,2]x\in[0,2]x∈[0,2],无需执行操作二;
x∈[3i,3i+1)x \in [3^i,3^{i+1})x∈[3i,3i+1),需要执行操作二i次,可以用数学归纳法证明。
执行0到2次操作三,再执行操作二,相当于将三进制的个位扔掉,十位及以上除以3,最高位为消失。
结论一:操作一和操作二的次数等于,x三进制的位数。
结论二:操作三的次数等于x三进制之和。
结论三:f(x)=x的三进制位数+x三进制各位之和。
性质一:x的三进制第i位不是2,且i最小,最低位是0。则x1∈[1,3i−1]x1\in[1,3^i-1]x1∈[1,3i−1],f(x+x1) <= f(x),无需枚举。
x+x1的第i1位+1,更高位不变,比i1低的位至少一个不是2。
性质二:x的三进制全部是2,最高位i。则f(x)==f(x+3i)。旧最高位2变0,新最高位0变1,位数加1。如果x1 < 3i。
则x+x1,比i1低的至少有一个不是2,故更小。
结论四:从x=L开始按性质一和性质二枚举,直到x >R。1e18不超过40个三进制位,每位至多枚举2轮,只有是最高位的时候,才会2变0。
故单个查询的时间一定小于O(200),故总时间复杂度:O(Q)。
uint64应该不会溢出。
代码
核心代码
#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>
#include<list>
#include<array>#include <bitset>
using namespace std;template<class T1, class T2>
std::istream& operator >> (std::istream& in, pair<T1, T2>& pr) {in >> pr.first >> pr.second;return in;
}template<class T1, class T2, class T3 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t);return in;
}template<class T1, class T2, class T3, class T4 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t);return in;
}template<class T = int>
vector<T> Read() {int n;cin >> n;vector<T> ret(n);for (int i = 0; i < n; i++) {cin >> ret[i];}return ret;
}
template<class T = int>
vector<T> ReadNotNum() {vector<T> ret;T tmp;while (cin >> tmp) {ret.emplace_back(tmp);if ('\n' == cin.get()) { break; }}return ret;
}template<class T = int>
vector<T> Read(int n) {vector<T> ret(n);for (int i = 0; i < n; i++) {cin >> ret[i];}return ret;
}template<int N = 1'000'000>
class COutBuff
{
public:COutBuff() {m_p = puffer;}template<class T>void write(T x) {int num[28], sp = 0;if (x < 0)*m_p++ = '-', x = -x;if (!x)*m_p++ = 48;while (x)num[++sp] = x % 10, x /= 10;while (sp)*m_p++ = num[sp--] + 48;AuotToFile();}void writestr(const char* sz) {strcpy(m_p, sz);m_p += strlen(sz);AuotToFile();}inline void write(char ch){*m_p++ = ch;AuotToFile();}inline void ToFile() {fwrite(puffer, 1, m_p - puffer, stdout);m_p = puffer;}~COutBuff() {ToFile();}
private:inline void AuotToFile() {if (m_p - puffer > N - 100) {ToFile();}}char puffer[N], * m_p;
};template<int N = 1'000'000>
class CInBuff
{
public:inline CInBuff() {}inline CInBuff<N>& operator>>(char& ch) {FileToBuf();while (('\r' == *S) || ('\n' == *S) || (' ' == *S)) { S++; }//忽略空格和回车ch = *S++;return *this;}inline CInBuff<N>& operator>>(int& val) {FileToBuf();int x(0), f(0);while (!isdigit(*S))f |= (*S++ == '-');while (isdigit(*S))x = (x << 1) + (x << 3) + (*S++ ^ 48);val = f ? -x : x; S++;//忽略空格换行 return *this;}inline CInBuff& operator>>(long long& val) {FileToBuf();long long x(0); int f(0);while (!isdigit(*S))f |= (*S++ == '-');while (isdigit(*S))x = (x << 1) + (x << 3) + (*S++ ^ 48);val = f ? -x : x; S++;//忽略空格换行return *this;}template<class T1, class T2>inline CInBuff& operator>>(pair<T1, T2>& val) {*this >> val.first >> val.second;return *this;}template<class T1, class T2, class T3>inline CInBuff& operator>>(tuple<T1, T2, T3>& val) {*this >> get<0>(val) >> get<1>(val) >> get<2>(val);return *this;}template<class T1, class T2, class T3, class T4>inline CInBuff& operator>>(tuple<T1, T2, T3, T4>& val) {*this >> get<0>(val) >> get<1>(val) >> get<2>(val) >> get<3>(val);return *this;}template<class T = int>inline CInBuff& operator>>(vector<T>& val) {int n;*this >> n;val.resize(n);for (int i = 0; i < n; i++) {*this >> val[i];}return *this;}template<class T = int>vector<T> Read(int n) {vector<T> ret(n);for (int i = 0; i < n; i++) {*this >> ret[i];}return ret;}template<class T = int>vector<T> Read() {vector<T> ret;*this >> ret;return ret;}
private:inline void FileToBuf() {const int canRead = m_iWritePos - (S - buffer);if (canRead >= 100) { return; }if (m_bFinish) { return; }for (int i = 0; i < canRead; i++){buffer[i] = S[i];//memcpy出错 }m_iWritePos = canRead;buffer[m_iWritePos] = 0;S = buffer;int readCnt = fread(buffer + m_iWritePos, 1, N - m_iWritePos, stdin);if (readCnt <= 0) { m_bFinish = true; return; }m_iWritePos += readCnt;buffer[m_iWritePos] = 0;S = buffer;}int m_iWritePos = 0; bool m_bFinish = false;char buffer[N + 10], * S = buffer;
};class Solution {public:vector<int> Ans(vector<pair<unsigned long long, unsigned long long>>& LR) {m_unit = { 1 };while (m_unit.back() <= ULLONG_MAX / 3) {m_unit.emplace_back(m_unit.back() * 3);}vector<int> ans;for (const auto& [l, r] : LR) {ans.emplace_back(Do(l, r));}return ans;}int Do(unsigned long long L, unsigned long long R) {m_v.clear();auto tmp = L;while (tmp) {m_v.emplace_back(tmp % 3);tmp /= 3;}int ans = 0;while (L <= R) {const int cur = m_v.size() + accumulate(m_v.begin(), m_v.end(), 0);ans = max(ans, cur);int inx = m_v.size();for (int i = 0; i < m_v.size(); i++) {if (2 != m_v[i]) {inx = i;break;}}if (m_v.size() == inx) {m_v.back() = 0;m_v.emplace_back(1);inx--;}else {m_v[inx]++;}L += m_unit[inx];}return ans;}vector< unsigned long long> m_unit;vector<int> m_v;};int main() {
#ifdef _DEBUGfreopen("a.in", "r", stdin);
#endif // DEBUG ios::sync_with_stdio(0); cin.tie(nullptr);//CInBuff<> in; COutBuff<10'000'000> ob; auto xe = Read<pair<unsigned long long, unsigned long long>>();
#ifdef _DEBUG //printf("N=%d", N);Out(xe ,",xe=");//Out(ab, ",ab=");//Out(B, "B=");//Out(que, "que=");//Out(B, "B=");
#endif // DEBUG auto res = Solution().Ans(xe);for (const auto& i : res){cout << i << "\n";}return 0;
};
单元测试
vector<pair<unsigned long long, unsigned long long>> LR;TEST_METHOD(TestMethod01){LR = { {2,3},{2,4},{9,10},{9,9} };auto res = Solution().Ans(LR);AssertV({ 3,4,5,4 }, res);}
数位DP
令三进制的最低位是第0位。f(x)=1 + 各数位之和(简称码和) + 最高位。
可以用数位DP求各码和数量,41位三进制,足够表示101810^{18}1018。dp[i][x]表示已经处理了i位,未处理的位的码和为x的数量。令M=41,x∈[0,3×M]x \in [0,3 \times M]x∈[0,3×M]
dp[N][0]=1,其它为0。
N-1位可以看成N位有一个前导零,但不同的位数,最高位不一样。故最高位不能为0。
vector<long long> cnt
cnt[i] 记录f(x)为i的数量。
cnt[f(left)]++
cnt[f(0∼r0 \sim r0∼r)]++
cnt[f(0∼left0\sim left0∼left)]–
查询的答案:最大i,cnt[i] >0。
单个出现时间复杂度:O(3M×3M)O(3M\times 3M)O(3M×3M)
核心代码
#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>
#include<list>
#include<array>#include <bitset>
using namespace std;template<class T1, class T2>
std::istream& operator >> (std::istream& in, pair<T1, T2>& pr) {in >> pr.first >> pr.second;return in;
}template<class T1, class T2, class T3 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t);return in;
}template<class T1, class T2, class T3, class T4 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t);return in;
}template<class T = int>
vector<T> Read() {int n;cin >> n;vector<T> ret(n);for (int i = 0; i < n; i++) {cin >> ret[i];}return ret;
}
template<class T = int>
vector<T> ReadNotNum() {vector<T> ret;T tmp;while (cin >> tmp) {ret.emplace_back(tmp);if ('\n' == cin.get()) { break; }}return ret;
}template<class T = int>
vector<T> Read(int n) {vector<T> ret(n);for (int i = 0; i < n; i++) {cin >> ret[i];}return ret;
}template<int N = 1'000'000>
class COutBuff
{
public:COutBuff() {m_p = puffer;}template<class T>void write(T x) {int num[28], sp = 0;if (x < 0)*m_p++ = '-', x = -x;if (!x)*m_p++ = 48;while (x)num[++sp] = x % 10, x /= 10;while (sp)*m_p++ = num[sp--] + 48;AuotToFile();}void writestr(const char* sz) {strcpy(m_p, sz);m_p += strlen(sz);AuotToFile();}inline void write(char ch){*m_p++ = ch;AuotToFile();}inline void ToFile() {fwrite(puffer, 1, m_p - puffer, stdout);m_p = puffer;}~COutBuff() {ToFile();}
private:inline void AuotToFile() {if (m_p - puffer > N - 100) {ToFile();}}char puffer[N], * m_p;
};template<int N = 1'000'000>
class CInBuff
{
public:inline CInBuff() {}inline CInBuff<N>& operator>>(char& ch) {FileToBuf();while (('\r' == *S) || ('\n' == *S) || (' ' == *S)) { S++; }//忽略空格和回车ch = *S++;return *this;}inline CInBuff<N>& operator>>(int& val) {FileToBuf();int x(0), f(0);while (!isdigit(*S))f |= (*S++ == '-');while (isdigit(*S))x = (x << 1) + (x << 3) + (*S++ ^ 48);val = f ? -x : x; S++;//忽略空格换行 return *this;}inline CInBuff& operator>>(long long& val) {FileToBuf();long long x(0); int f(0);while (!isdigit(*S))f |= (*S++ == '-');while (isdigit(*S))x = (x << 1) + (x << 3) + (*S++ ^ 48);val = f ? -x : x; S++;//忽略空格换行return *this;}template<class T1, class T2>inline CInBuff& operator>>(pair<T1, T2>& val) {*this >> val.first >> val.second;return *this;}template<class T1, class T2, class T3>inline CInBuff& operator>>(tuple<T1, T2, T3>& val) {*this >> get<0>(val) >> get<1>(val) >> get<2>(val);return *this;}template<class T1, class T2, class T3, class T4>inline CInBuff& operator>>(tuple<T1, T2, T3, T4>& val) {*this >> get<0>(val) >> get<1>(val) >> get<2>(val) >> get<3>(val);return *this;}template<class T = int>inline CInBuff& operator>>(vector<T>& val) {int n;*this >> n;val.resize(n);for (int i = 0; i < n; i++) {*this >> val[i];}return *this;}template<class T = int>vector<T> Read(int n) {vector<T> ret(n);for (int i = 0; i < n; i++) {*this >> ret[i];}return ret;}template<class T = int>vector<T> Read() {vector<T> ret;*this >> ret;return ret;}
private:inline void FileToBuf() {const int canRead = m_iWritePos - (S - buffer);if (canRead >= 100) { return; }if (m_bFinish) { return; }for (int i = 0; i < canRead; i++){buffer[i] = S[i];//memcpy出错 }m_iWritePos = canRead;buffer[m_iWritePos] = 0;S = buffer;int readCnt = fread(buffer + m_iWritePos, 1, N - m_iWritePos, stdin);if (readCnt <= 0) { m_bFinish = true; return; }m_iWritePos += readCnt;buffer[m_iWritePos] = 0;S = buffer;}int m_iWritePos = 0; bool m_bFinish = false;char buffer[N + 10], * S = buffer;
};template<class ELE, class ResultType>
class IUpperDPCall
{
public:virtual void OnEnum(vector<ResultType>& dp, const vector<ResultType>& vNext, ELE curValue, int iCustomStatusCount) = 0;virtual void OnInitEnd(vector<ResultType>& dp, vector<ResultType>& upDp) = 0;
};template<class ELE, class ResultType>
class CUperrDP2
{
public:CUperrDP2(int iCustomStatusCount, IUpperDPCall<ELE, ResultType>& call, ELE minEle, ELE maxEle, ELE firstMinEle) :m_iCustomStatusCount(iCustomStatusCount), m_call(call), m_minEle(minEle),m_maxEle(maxEle),m_firstMinEle(firstMinEle){}void Init(const ELE* pHigh, int iEleCount){m_vDP.assign(iEleCount + 1, vector<ResultType>(m_iCustomStatusCount));m_vDpUpper = m_vDP;m_vFirstDP = m_vDP;m_call.OnInitEnd(m_vDP.back(), m_vDpUpper.back());//预处理增加的一位for (int i = iEleCount - 1;i > 0;i--) {m_call.OnEnum(m_vDpUpper[i], m_vDpUpper[i + 1], pHigh[i], m_iCustomStatusCount);for (auto j = m_minEle; j < pHigh[i];j++) {m_call.OnEnum(m_vDpUpper[i], m_vDP[i + 1], j, m_iCustomStatusCount);}for (auto j = m_minEle; j <= m_maxEle;j++) {m_call.OnEnum(m_vDP[i], m_vDP[i + 1], j, m_iCustomStatusCount);}for (auto j = m_firstMinEle; j <= m_maxEle;j++) {m_call.OnEnum(m_vFirstDP[i], m_vDP[i + 1], j, m_iCustomStatusCount);}}m_call.OnEnum(m_vFirstDP[0], m_vDpUpper[1], pHigh[0], m_iCustomStatusCount);for (auto j = m_firstMinEle; j < pHigh[0];j++) {m_call.OnEnum(m_vFirstDP[0], m_vDP[1], j, m_iCustomStatusCount);}}ResultType Sum(int iMinCustomStatu, int iMaxCustomStatu) {ResultType ret = 0;for (int i = 0;i + 1 < m_vFirstDP.size();i++) {ret += accumulate(m_vFirstDP[i].begin() + iMinCustomStatu, m_vFirstDP[i].begin() + iMaxCustomStatu + 1, (ResultType)0);}return ret;}ResultType Sum() {return Sum(0, m_iCustomStatusCount - 1);}vector<vector<ResultType>> m_vDP, m_vDpUpper,m_vFirstDP;ELE m_minEle, m_maxEle, m_firstMinEle;
protected:const int m_iCustomStatusCount;IUpperDPCall<ELE, ResultType>& m_call;
};template<class ELE, class ResultType>class CCall :public IUpperDPCall<ELE, ResultType>{public: virtual void OnInitEnd(vector<ResultType>& dp, vector<ResultType>& upDp){dp[0] = upDp[0] = 1;}virtual void OnEnum(vector<ResultType>& dp, const vector<ResultType>& vNext, ELE curValue, int iCustomStatusCount){const int cur = curValue - '0';for (int i = cur;i < iCustomStatusCount;i++) { dp[i] += vNext[i-cur];}}vector<vector<int>> m_vTo;int m_iK;};class Solution {public:vector<int> Ans(vector<pair<unsigned long long, unsigned long long>>& LR) {vector<int> ans;for (const auto& [l, r] : LR) {ans.emplace_back(Do(l, r)+1);}return ans;}int Do(unsigned long long L, unsigned long long R) {CCall<char, long long> call;CUperrDP2<char, long long> dp1(3 * M + 1, call, '0', '2','1'), dp2(3 * M + 1, call, '0', '2','1');string str1 = To3(L);string str2 = To3(R);dp1.Init(str1.c_str(), str1.length());dp2.Init(str2.c_str(), str2.length());int y3 = str1.length()-1;for (const auto& ch : str1) {y3 += ch - '0';}vector<long long> cnt(4 * M);cnt[y3]++;for (int i = 0; i < str1.size();i++){for (int j = M * 3; j >= 0;j--){ cnt[str1.size() - 1 - i + j] -= dp1.m_vFirstDP[i][j];}}for (int i = 0; i < str2.size();i++){for (int j = M * 3; j >= 0;j--){cnt[str2.size() - 1 - i + j] += dp2.m_vFirstDP[i][j];}}for (int j = cnt.size()-1; j >= 0;j--){if (cnt[j] > 0) { return j; }}return 0;}string To3(unsigned long long L) {string str;while (L) {str += ('0' + L % 3);L /= 3;}if ("" == str) { str = "0"; }return string(str.rbegin(), str.rend());}vector< unsigned long long> m_unit;vector<int> m_v;const int M = 41;};int main() {
#ifdef _DEBUGfreopen("a.in", "r", stdin);
#endif // DEBUG ios::sync_with_stdio(0); cin.tie(nullptr);//CInBuff<> in; COutBuff<10'000'000> ob; auto xe = Read<pair<unsigned long long, unsigned long long>>();
#ifdef _DEBUG //printf("N=%d", N);Out(xe ,",xe=");//Out(ab, ",ab=");//Out(B, "B=");//Out(que, "que=");//Out(B, "B=");
#endif // DEBUG auto res = Solution().Ans(xe);for (const auto& i : res){cout << i << "\n";}return 0;
};
扩展阅读
我想对大家说的话 |
---|
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。 |
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作 |
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注 |
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。 |
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。 |
如果程序是一条龙,那算法就是他的是睛 |
失败+反思=成功 成功+反思=成功 |
视频课程
先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176
测试环境
操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。