Exact Power-of-Two Expression
National Olympiad — Regional round (2021 season)
Submission name: expr.cpp
Problem summary
Compute the exact integer 2^n−m, where m may be negative and may have thousands of digits.
Input
The exponent n and signed decimal integer m.
Output
The exact decimal value of 2^n−m.
Constraints
1 ≤ n ≤ 10000; −2^n ≤ m ≤ 2^n.
Worked example
Input and output for the worked example discussed below.
Input
6 3
Output
61
Solution analysis
- Ordinary integer types cannot hold 2^10000. Represent nonnegative integers as decimal strings. Begin with power="1" and double it n times, carrying from the least significant digit toward the front.
- If m is negative, the expression becomes 2^n+|m|, so use grade-school addition. Otherwise subtract m from the power using a borrow. The source guarantees m≤2^n, so this subtraction is never negative.
- For n=6, doubling produces 1,2,4,8,16,32,64. Subtracting 3 gives 61. For n=3 and m=−7, add 7 to 8 instead, giving 15. Remove redundant leading zeros but keep one zero for a zero result.
Why it works
Each string operation implements the positional base-ten carry or borrow rule, preserving the exact represented integer. Induction over the n doublings gives 2^n. The final sign-dependent addition or subtraction is algebraically identical to 2^n−m.
Watch out. Do not parse m with stoll or use floating-point pow. Handle a leading minus sign, borrow chains, and the exact zero case.
Commented C++17 solution
An independently written complete program with standard headers.
#include<algorithm>
#include<iostream>
#include<string>
using namespace std;
string add(string a,string b){
reverse(a.begin(),a.end());
reverse(b.begin(),b.end());
string result;
int carry=0;
for(size_t i=0;i<max(a.size(),b.size())||carry;i++){
int sum=carry;
if(i<a.size()){
sum+=a[i]-'0';
}
if(i<b.size()){
sum+=b[i]-'0';
}
result.push_back(char('0'+sum%10));
carry=sum/10;
}
reverse(result.begin(),result.end());
return result;
}
string subtract(string a,string b){
reverse(a.begin(),a.end());
reverse(b.begin(),b.end());
int borrow=0;
for(size_t i=0;i<a.size();i++){
int digit=a[i]-'0'-borrow;
if(i<b.size()){
digit-=b[i]-'0';
}
borrow=digit<0;
if(borrow){
digit+=10;
}
a[i]=char('0'+digit);
}
while(a.size()>1&&a.back()=='0'){
a.pop_back();
}
reverse(a.begin(),a.end());
return a;
}
int main(){
int exponent;
string m;
cin>>exponent>>m;
string power="1";
for(int i=0;i<exponent;i++){
// Doubling the exact string avoids floating-point rounding.
power=add(power,power);
}
if(m[0]=='-'){
cout<<add(power,m.substr(1))<<'\n';
}
else{
cout<<subtract(power,m)<<'\n';
}
}