#P3770. 第2题-合并序列
          
                        
                                    
                      
        
              - 
          
          
                      1000ms
            
          
                      Tried: 63
            Accepted: 13
            Difficulty: 4
            
          
          
          
                       所属公司 : 
                              字节
                                
            
                        
              时间 :2025年9月21日
                              
                      
          
 
- 
                        算法标签>数学          
 
第2题-合并序列
解题思路
- 
观察:每次把一个
x≥1∑cnt[x]⋅kx−1=n1加到序列末尾;若有k个相邻且相等为x,就合并为一个x+1。这个过程与“k 进制加一并进位”完全一致: 将序列中“值为x的元素个数”记为cnt[x],始终有(一次合并把
k个x变成一个x+1,两边权重都等于k\cdot k^{x-1}=k^x)。 - 
终止态性质:若还存在
k个相邻的x就能继续合并,所以终止时对所有x都有0\le cnt[x]<k。而且序列在整个过程中保持非增(左大右小),因此最终形态一定是从大到小的“分块”:L、L-1、…、1,每个值各出现cnt[x]次。 - 
于是
cnt[x]正是n的 k 进制展开 的各位数字: 令n = a_0 + a_1 k + a_2 k^2 + ...,其中0 ≤ a_i < k,则cnt[i+1]=a_i。 最终答案就是把数值从大到小输出:对i从最高位到 0,输出(i+1)这个十进制串,重复a_i次并拼接。 - 
算法要点:
- 先把 
n转为k进制得到各位a_i = n % k; - 从高位到低位依次把字符串 
str(i+1)复制a_i次拼到答案上。 这避免了实际模拟,正确且高效。 
 - 先把 
 
复杂度分析
- 时间复杂度:将 
n转为k进制需要O(log_k n),输出本身的长度记为AnsLen,总体O(log_k n + AnsLen)。 - 空间复杂度:存放各位数字,需要 
O(log_k n)。 
代码实现
Python
# 题面功能放在函数里,主函数只负责读写
import sys
def build_answer(n: int, k: int) -> str:
    # 将 n 转为 k 进制,得到各位 a_i(低位在前)
    digits = []
    while n > 0:
        digits.append(n % k)
        n //= k
    # 从高位到低位输出 (i+1) ,重复 a_i 次
    res_parts = []
    for i in range(len(digits) - 1, -1, -1):
        cnt = digits[i]
        if cnt == 0:
            continue
        val_str = str(i + 1)  # 当前块的值
        res_parts.append(val_str * int(cnt))
    return "".join(res_parts)
def main():
    data = sys.stdin.read().strip().replace('\n', ' ').split()
    n = int(data[0])
    k = int(data[1])
    print(build_answer(n, k))
if __name__ == "__main__":
    main()
Java
// 类名必须为 Main(ACM 风格)
import java.io.*;
import java.util.*;
public class Main {
    // 功能函数:根据 n、k 构造答案
    static String buildAnswer(long n, int k) {
        // 保存 k 进制各位(低位在前)
        ArrayList<Integer> digits = new ArrayList<>();
        while (n > 0) {
            digits.add((int)(n % k));
            n /= k;
        }
        StringBuilder sb = new StringBuilder();
        // 从高位到低位输出 (i+1),重复 a_i 次
        for (int i = digits.size() - 1; i >= 0; --i) {
            int cnt = digits.get(i);
            if (cnt == 0) continue;
            String val = Integer.toString(i + 1);
            for (int t = 0; t < cnt; ++t) sb.append(val);
        }
        return sb.toString();
    }
    public static void main(String[] args) throws Exception {
        // 数据范围不大,使用常规输入即可
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String line = br.readLine();
        String[] parts = line.trim().split("\\s+");
        long n = Long.parseLong(parts[0]);
        int k = Integer.parseInt(parts[1]);
        System.out.print(buildAnswer(n, k));
    }
}
C++
// ACM 风格,读入后调用外部函数完成计算
#include <bits/stdc++.h>
using namespace std;
// 功能函数:根据 n、k 构造答案字符串
string buildAnswer(unsigned long long n, unsigned long long k) {
    vector<unsigned int> digits; // k 进制各位(低位在前)
    while (n > 0) {
        digits.push_back((unsigned int)(n % k));
        n /= k;
    }
    string res;
    // 从高位到低位输出 (i+1),重复 a_i 次
    for (int i = (int)digits.size() - 1; i >= 0; --i) {
        unsigned int cnt = digits[i];
        if (cnt == 0) continue;
        string val = to_string(i + 1); // 当前块的值
        for (unsigned int t = 0; t < cnt; ++t) res += val;
    }
    return res;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    unsigned long long n, k;
    if (!(cin >> n >> k)) return 0;
    cout << buildAnswer(n, k);
    return 0;
}
        题目内容
首先,你有一个为空的序列,接下来会进行 n 次操作:
每次操作:将一个单独的数字 1 放入序列末尾;
如果序列中出现 k 个相邻的元素都等于 x ,则这 k 个元素会合并成一个元素,值为 x+1 ;
合并后可能会触发新的合并操作,直到序列中不存在可合并区间为止。
定义最终序列中的元素依次拼接成一个十进制数,作为答案输出。
输入描述
在一行上输入两个整数 n,k(1≦n≦1018;2≦k≦2×105) ,分别表示操作总次数,合并阈值。
输出描述
在一行上输出一个整数,表示最终序列中所有元素从左到右拼接形成的十进制数。
样例1
输入
5 2
输出
3 1
说明
在这个样例中,操作如下:
- 
第一次,序列由 { } 变为 { 1 };
 - 
第二次,序列由 {1} 变为 {1,1},随后两个相邻的 1 合并成 {2};
 - 
第三次,序列由 {2} 变为 {2,1};
 - 
第四次,序列由 {2,1} 变为 {2,1,1},先合并 {1,1}→2 得 {2,2},再合并 2,2→3 得 {3 };
 - 
第五次:序列由 {3} 变为 {3,1}。