1 条题解
-
0
题解思路
这是一道典型的数位动态规划问题,核心在于处理超大整数的约束计数,通过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; }
算法正确性保证:
- 完备性:所有可能的数字构造路径都被考虑
- 互斥性:tight和not-tight状态互不重叠
- 约束满足:每一位的选择都严格遵守约束条件
详细算法设计
预处理阶段:
- 将输入的二进制字符串转换为位约束数组
- 检查约束一致性,及早发现冲突情况
- 处理超出位数范围的约束
动态规划阶段:
- 从最高位开始逐位构造
- 维护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
- 上传者