1 条题解

  • 0
    @ 2025-9-14 18:58:21

    题解思路

    这是一道典型的数位动态规划问题,核心在于处理超大整数的约束计数,通过tight状态精确控制搜索边界。

    核心算法分析

    数位DP状态设计: 我们需要维护两个关键状态:

    • tight状态:当前构造的数字前缀是否与n的前缀完全相等
    • 约束满足:每一位是否满足给定的约束条件

    状态转移方程: 设dp[i][tight]表示处理到第i位(从高位开始),tight状态下的合法数字个数。

    dp[i+1][new_tight] += dp[i][tight]
    

    其中new_tight的计算规则:

    • 如果当前位选择的数字小于n的对应位,则new_tight = false
    • 如果当前位选择的数字等于n的对应位,则new_tight保持原值
    • 如果当前位选择的数字大于n的对应位,则这种选择非法

    约束处理策略

    预处理约束

    int[] constraints = new int[bitLength];
    Arrays.fill(constraints, -1); // -1表示无约束
    
    for (int i = 0; i < m; i++) {
        int position = constraints_input[i][0];
        int value = constraints_input[i][1];
        
        if (position >= bitLength) {
            // 约束位置超出n的位数,只有约束为0才可能满足
            if (value == 1) return 0;
            continue;
        }
        
        int highBitIndex = bitLength - 1 - position;
        if (constraints[highBitIndex] != -1 && constraints[highBitIndex] != value) {
            return 0; // 约束冲突
        }
        constraints[highBitIndex] = value;
    }
    

    状态转移核心逻辑

    for (int bit = 0; bit < bitLength; bit++) {
        int nBit = nBinary.charAt(bit) - '0';
        Set<Integer> allowedDigits = new HashSet<>();
        
        if (constraints[bit] == -1) {
            allowedDigits.add(0);
            allowedDigits.add(1);
        } else {
            allowedDigits.add(constraints[bit]);
        }
        
        long newNotTight = 0, newTight = 0;
        
        // 从不紧约束状态转移
        if (notTightCount > 0) {
            newNotTight = (newNotTight + (long)notTightCount * allowedDigits.size()) % MOD;
        }
        
        // 从紧约束状态转移
        if (tightCount > 0) {
            for (int digit : allowedDigits) {
                if (digit < nBit) {
                    newNotTight = (newNotTight + tightCount) % MOD;
                } else if (digit == nBit) {
                    newTight = (newTight + tightCount) % MOD;
                }
            }
        }
        
        notTightCount = (int)newNotTight;
        tightCount = (int)newTight;
    }
    

    算法正确性保证

    1. 完备性:所有可能的数字构造路径都被考虑
    2. 互斥性:tight和not-tight状态互不重叠
    3. 约束满足:每一位的选择都严格遵守约束条件

    详细算法设计

    预处理阶段

    • 将输入的二进制字符串转换为位约束数组
    • 检查约束一致性,及早发现冲突情况
    • 处理超出位数范围的约束

    动态规划阶段

    • 从最高位开始逐位构造
    • 维护tight/not-tight两种状态的计数
    • 根据约束条件筛选合法的数字选择

    边界处理

    • 正确处理n的最高位
    • 处理约束位置超出n位数的特殊情况
    • 模运算防止整数溢出

    Java代码实现

    import java.io.*;
    import java.util.*;
    
    public class Main {
        private static final int MODULUS = 998244353;
        
        private static long solveBinaryConstraints(String nBinary, List<int[]> restrictionList) {
            int binaryLength = nBinary.length();
            int[] positionConstraints = new int[binaryLength];
            Arrays.fill(positionConstraints, -1); // -1 表示该位无约束
            
            // 预处理所有位置约束
            for (int[] restriction : restrictionList) {
                int lowBitPosition = restriction[0];
                int requiredValue = restriction[1];
                
                // 检查约束位置是否超出n的位数
                if (lowBitPosition >= binaryLength) {
                    if (requiredValue == 1) {
                        return 0; // 要求高位为1,但n的该位为0,无解
                    }
                    continue; // 要求高位为0,自动满足
                }
                
                // 转换为从高位开始的索引
                int highBitIndex = binaryLength - 1 - lowBitPosition;
                
                // 检查约束一致性
                if (positionConstraints[highBitIndex] != -1 && 
                    positionConstraints[highBitIndex] != requiredValue) {
                    return 0; // 约束冲突
                }
                
                positionConstraints[highBitIndex] = requiredValue;
            }
            
            // 数位DP状态:[当前位][是否紧贴上界]
            long countNotTight = 0; // 前缀已经小于n的对应前缀
            long countTight = 1;    // 前缀等于n的对应前缀
            
            // 从最高位开始逐位处理
            for (int bitPosition = 0; bitPosition < binaryLength; bitPosition++) {
                int nCurrentBit = nBinary.charAt(bitPosition) - '0';
                
                // 确定当前位的合法选择
                List<Integer> validChoices = new ArrayList<>();
                if (positionConstraints[bitPosition] == -1) {
                    validChoices.add(0);
                    validChoices.add(1);
                } else {
                    validChoices.add(positionConstraints[bitPosition]);
                }
                
                long nextCountNotTight = 0;
                long nextCountTight = 0;
                
                // 从"已经小于"状态转移
                if (countNotTight > 0) {
                    nextCountNotTight = (nextCountNotTight + 
                                       countNotTight * validChoices.size()) % MODULUS;
                }
                
                // 从"紧贴上界"状态转移
                if (countTight > 0) {
                    for (int choice : validChoices) {
                        if (choice < nCurrentBit) {
                            nextCountNotTight = (nextCountNotTight + countTight) % MODULUS;
                        } else if (choice == nCurrentBit) {
                            nextCountTight = (nextCountTight + countTight) % MODULUS;
                        }
                        // choice > nCurrentBit 的情况直接跳过(非法)
                    }
                }
                
                countNotTight = nextCountNotTight;
                countTight = nextCountTight;
            }
            
            return (countNotTight + countTight) % MODULUS;
        }
        
        public static void main(String[] args) throws IOException {
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            
            String nBinaryStr = reader.readLine().trim();
            int constraintCount = Integer.parseInt(reader.readLine());
            
            List<int[]> constraintList = new ArrayList<>();
            
            for (int i = 0; i < constraintCount; i++) {
                String[] parts = reader.readLine().split(" ");
                int position = Integer.parseInt(parts[0]);
                int value = Integer.parseInt(parts[1]);
                constraintList.add(new int[]{position, value});
            }
            
            long result = solveBinaryConstraints(nBinaryStr, constraintList);
            System.out.println(result);
            
            reader.close();
        }
    }
    

    C++代码实现

    #include <iostream>
    #include <vector>
    #include <string>
    #include <unordered_set>
    #include <algorithm>
    using namespace std;
    
    const int MOD = 998244353;
    
    class BinaryCountingSolver {
    private:
        static long long computeValidNumbers(const string& upperBound, 
                                           const vector<pair<int, int>>& bitConstraints) {
            int totalBits = upperBound.length();
            vector<int> bitRequirements(totalBits, -1);
            
            // 解析并验证所有位约束
            for (const auto& constraint : bitConstraints) {
                int lowOrderPosition = constraint.first;
                int mandatoryValue = constraint.second;
                
                // 处理超出上界位数的约束
                if (lowOrderPosition >= totalBits) {
                    if (mandatoryValue == 1) {
                        return 0; // 不可能满足的约束
                    }
                    continue; // 自动满足的约束
                }
                
                // 转换为高位优先的位置索引
                int highOrderPosition = totalBits - 1 - lowOrderPosition;
                
                // 约束一致性检查
                if (bitRequirements[highOrderPosition] != -1 && 
                    bitRequirements[highOrderPosition] != mandatoryValue) {
                    return 0; // 约束矛盾
                }
                
                bitRequirements[highOrderPosition] = mandatoryValue;
            }
            
            // 数位DP:维护两种状态的计数
            long long looseBoundCount = 0;  // 前缀严格小于上界
            long long tightBoundCount = 1;  // 前缀等于上界前缀
            
            // 逐位构造合法数字
            for (int currentBit = 0; currentBit < totalBits; currentBit++) {
                int upperBoundBit = upperBound[currentBit] - '0';
                
                // 收集当前位的所有合法选择
                vector<int> feasibleDigits;
                if (bitRequirements[currentBit] == -1) {
                    feasibleDigits.push_back(0);
                    feasibleDigits.push_back(1);
                } else {
                    feasibleDigits.push_back(bitRequirements[currentBit]);
                }
                
                long long newLooseCount = 0;
                long long newTightCount = 0;
                
                // 状态转移:从loose状态
                if (looseBoundCount > 0) {
                    newLooseCount = (newLooseCount + 
                                   looseBoundCount * (long long)feasibleDigits.size()) % MOD;
                }
                
                // 状态转移:从tight状态
                if (tightBoundCount > 0) {
                    for (int digit : feasibleDigits) {
                        if (digit < upperBoundBit) {
                            newLooseCount = (newLooseCount + tightBoundCount) % MOD;
                        } else if (digit == upperBoundBit) {
                            newTightCount = (newTightCount + tightBoundCount) % MOD;
                        }
                        // digit > upperBoundBit 情况被自动排除
                    }
                }
                
                looseBoundCount = newLooseCount;
                tightBoundCount = newTightCount;
            }
            
            return (looseBoundCount + tightBoundCount) % MOD;
        }
        
    public:
        static long long solve(const string& nBinary, 
                              const vector<pair<int, int>>& constraints) {
            return computeValidNumbers(nBinary, constraints);
        }
    };
    
    int main() {
        ios_base::sync_with_stdio(false);
        cin.tie(nullptr);
        
        string nBinaryRepresentation;
        cin >> nBinaryRepresentation;
        
        int numberOfConstraints;
        cin >> numberOfConstraints;
        
        vector<pair<int, int>> allConstraints;
        
        for (int i = 0; i < numberOfConstraints; i++) {
            int bitPosition, requiredBit;
            cin >> bitPosition >> requiredBit;
            allConstraints.emplace_back(bitPosition, requiredBit);
        }
        
        long long finalAnswer = BinaryCountingSolver::solve(nBinaryRepresentation, allConstraints);
        cout << finalAnswer << endl;
        
        return 0;
    }
    

    时间复杂度:O(L + m),其中L为二进制位数,m为约束数量 空间复杂度:O(L),用于存储位约束信息

    • 1

    信息

    ID
    293
    时间
    1000ms
    内存
    256MiB
    难度
    6
    标签
    递交数
    1
    已通过
    1
    上传者