Bits
How to convert from decimal to any number system
vector<long long>getRepresentation(long long n, int base){
vector<long long>ret;
while(n){
ret.push_back(n%base);
n/=base;
}
return ret;
}
Bitwise operations
| Operator | Description |
|---|---|
| & | Bitwise AND |
| | | Bitwise OR |
| ^ | Bitwise XOR |
| » | Bitwise right shifting |
| « | Bitwise left shifting |
| ~ | one’s complement |
| AND | gives 1 if all are 1 |
|
|---|---|---|
| OR | gives 1 if there are atleast 1 |
|
| XOR | give one if number of 1s is odd | |
| Complement | flips all bits |
Left-shifting
nbyk(n << k) multipliesnby2^k. Right-shiftingnbyk(n >> k) dividesnby2^k(integer division, remainder dropped).
Helpful functions
to check weather the bit is 1 or 0
bool checkBit(ll n,ll i){
return (n >> i)&1;
}
to check the number is odd or even
void checkParity(long long n){
cout<<(n&1 ? "odd" : "even");
}
to toggle a certain bit
long long toggleBit(long long n, long long bit){
return (n^(1ll<<bit));
}
to set a certain bit
long long setBit(long long n, long long bit){
return (n|(1ll<<bit));
}
number of set bits
int countOnes(long long x){
return __builtin_popcountll(x);
}
helpful information
$$2^n = 1 + \sum_{i<n} 2^i$$
One bit at position n > all bits below it combined
so if there any budget or getting highest number by changing bit so change the higher bit is more effective more that changing all the other bits
H. Maximal AND
this problem is apply this concept by giving an array of n numbers and k number of operations and we want to get the maximum number we can get by ANDING(&) all the the array members after flipping bits from 30th bit to the first bit so the information that will help us to get greedy is One bit at position n > all bits below it combined so we check if we could flip the 30th bit so the ANDING as big as possible if we could not do it we go to the 29th and so on until get the farest as possible
long long ans = 0;
for(int bit = 30; bit>=0; bit--){
ll cost = 0;
for(auto &x : a ){
if (!checkBit(x, bit))cost++;
}
if (cost<=k){
k-=cost;
ans+=1<<bit;
}
}
time complexity O(31 · n)
prefix sum for (XOR,AND and OR)
A XOR A = 0
that help us to cancel the extra part we add we we get the prefix sum of a range form [l,r] we will add the prefix to r XOR prefix[l-1]
prefix sum (XOR)
int n;cin>>n;
vector<long long>v(n+1);
vector<long long>prefXOR(n+1);
for(int i = 1; i <=n; i++)cin>>v[i];
for(int i = 1; i<= n; i++)prefXOR[i]=prefXOR[i-1]^v[i];
int q;cin>>q;
while(q--){
int l,r;cin>>l>>r;
cout<<prefXOR[r]^prefXOR[l-1]<<'\n';
}
time complexity building O(n) query O(1)
but we can’t do the same for OR and AND but we could count the number of 1 that appears for every bit and in the case of AND we just will ask the number of seted bits are equals to the range or not (all the bits in this range are 1s) in the case of OR we just ask about if there any 1 appears on that specific range of that bit
prefix sum (AND)
bool checkBit(long long n,long long bit){
return (n >> bit)&1;
}
int n;cin>>n;
vector<long long>v(n+1);
for(int i = 1; i<=n; i++)cin>>v[i];
vector<vector<long long >>prefixBits(64,vector<long long>(n+1));
for(int bit = 0; bit < 64; bit++){
for(long long i = 1; i<=n; i++){
prefixBits[bit][i]=prefixBits[bit][i-1]+((v[i]>>bit)&1);
}
}
long long q;cin>>q;
while(q--){
long long l,r;cin>>l>>r;
long long ans = 0;
for(int bit = 0; bit < 64; bit++){
int cnt=prefixBits[bit][r]-prefixBits[bit][l-1];
if(cnt>=r-(l-1))ans+=(1ll<<bit);
}
cout<<ans<<'\n';
}
time complexity building O(n · 64) query O(64)
prefix sum (OR)
bool checkBit(long long n,long long bit){
return (n >> bit)&1;
}
int n;cin>>n;
vector<long long>v(n+1);
for(int i = 1; i<=n; i++)cin>>v[i];
vector<vector<long long >>prefixBits(64,vector<long long>(n+1));
for(int bit = 0; bit < 64; bit++){
for(long long i = 1; i<=n; i++){
prefixBits[bit][i]=prefixBits[bit][i-1]+((v[i]>>bit)&1);
}
}
long long q;cin>>q;
while(q--){
long long l,r;cin>>l>>r;
long long ans = 0;
for(int bit = 0; bit < 64; bit++){
int cnt=prefixBits[bit][r]-prefixBits[bit][l-1];
if(cnt>0)ans+=(1ll<<bit);
}
cout<<ans<<'\n';
}
time complexity building O(n · 64) query O(64)
E. Iva & Pav
For a query (l, k), you fix l and slide r from l up to n. For each r, you compute f(l, r) = a[l] & a[l+1] & ... & a[r]. You want the largest r such that this AND value is still ≥ k.
we know how to get the prefix sum of ANDING operation on a specific range from above but we know have a real problem we could not try all the possible r the worst case will take a large time and will exceed the time limit because we have a lot of queries
so we want to minimize the time will be taken to search for r and we got lucky because AND result is monotonic so we could do a binary search to get the right r faster.
so the code will be
typedef long long ll;
#define vi vector<ll>
#define el "\n"
bool checkBit(ll n,ll i){
return (n >> i)&1;
}
void solve() {
ll n;cin>>n;
vi a(n+1);
vector<vector<ll>> pref(32,vector<ll>(n+1));
for(int i =1; i<=n; i++)cin>>a[i];
for(int bit = 0; bit<32; bit++){
for(int j = 1; j<=n; j++){
pref[bit][j]= pref[bit][j-1]+checkBit(a[j], bit);
}
}
ll q;cin>>q;
while(q--){
ll st,k;cin>>st>>k;
ll l = st, r= n;
ll ans =-1;
while (r>=l){
ll mid = l+(r-l)/2;
ll andValue=0;
for(int bit = 0; bit<32; bit++){
ll count1=pref[bit][mid]-pref[bit][st-1];
if(count1==mid-st+1)andValue+=1ll<<bit;
}
if (andValue>=k)ans=mid,l =mid+1;
else r=mid-1;
}
cout<<ans<<" ";
}
cout<<el;
}
time complexity building the pref O(32 . n) , query O(32 . log n) the 32 for the building and query is contain so we could remove it and we have q number of queries so the total time compelxity is O(n+(q.log n))
AND monotonic decreasing , OR monotonic increasing XOR and XOR is not monotonic
Bit mask
**bit mask is just a number make no scence if we look at it as a decimal representation but if we looked at it’s binary representation we could see that it means takes that leaves that so it be really helpful at the subset problemes **
B. Preparing Olympiad
given n problems (n ≤ 15) each with a difficulty, we want to count how many subsets of at least 2 problems have total difficulty between l and r, and the difference between hardest and easiest problem in the subset is at least x.
since n ≤ 15, there are only 2^15 = 32768 possible subsets, so we can just try all of them with a bitmask and check the conditions directly on each one.
for each mask go through every bit i and check if it’s set if so include that element in the sum and update the running min/max for that subset. after checking all bits test if sum is in [l,r] and max-min >= x if so count it.
void solve() {
ll n,l,r,x;cin>>n>>l>>r>>x;
vi c(n);
for(auto &val : c) cin>>val;
ll ans = 0;
for(int mask = 0; mask < 1<<n; mask++){
ll sum = 0, mn = oo, mx = -oo;
for(int i = 0; i < n; i++){
if(mask >> i & 1){
sum += c[i];
mn = min(mn, c[i]);
mx = max(mx, c[i]);
}
}
if(sum >= l && sum <= r && (mx - mn) >= x){
ans++;
}
}
cout << ans << el;
}
time complexity is O($2^n$ . n), and n up to 15 .
helpful builtin functions
__builtin_clz(x)
This function is used to count the leading zeros of the integer. Note : clz = count leading zero’s.
Example: It counts number of zeros before the first occurrence of one(set bit).
a = 16 Binary form of 16 is 00000000 00000000 00000000 00010000
Output: 27
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n = 16;
cout<<"Count of leading zeros before 1 in "<< n <<" is "<<__builtin_clz(n);
return 0;
}
Output
Count of leading zeros before 1 in 16 is 27
Note: __builtin_clz(x) This function only accept unsigned values
Note: Similarly you can use __builtin_clzl(x) & __builtin_clzll(x) for long and long long data types.
__builtin_ctz(x)
This function is used to count the trailing zeros of the given integer. Note : ctz = count trailing zeros.
Example: Count no of zeros from last to first occurrence of one(set bit).
a = 16
Binary form of 16 is 00000000 00000000 00000000 00010000
Output: ctz = 4
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n = 16;
cout<<"Count of zeros from last to first occurrence of one is "<< __builtin_ctz(n);
return 0;
}
Output
Count of zeros from last to first occurrence of one is 4
Note: Similarly you can use __builtin_ctzl(x) & __builtin_ctzll(x) for long and long long data types.
Bitset
is a container that represents a fixed-size sequence of bits. A bitset allows you to manipulate individual bits efficiently, making it useful in problems related to bitwise operations, such as checking flags, implementing binary representations.
Bitset is defined as the std::bitset class template inside the < bitset > header file.
helpful functions for < bitset >
| Syntax | What it does |
|---|---|
bitset<N> b |
creates a bitset of fixed size N, all bits initialized to 0 |
bitset<N> b(x) |
creates a bitset of size N initialized from integer x |
bitset<N> b(str) |
creates a bitset from a binary string, e.g. "1011" |
b[i] |
access/set bit at position i (like an array) |
b.set() |
sets all bits to 1 |
b.set(i) |
sets bit i to 1 |
b.set(i, val) |
sets bit i to val (0 or 1) |
b.reset() |
sets all bits to 0 |
b.reset(i) |
sets bit i to 0 |
b.flip() |
flips all bits |
b.flip(i) |
flips bit i |
b.test(i) |
returns true if bit i is set (bounds-checked, throws if out of range) |
b.count() |
returns the number of set bits (1s) |
b.size() |
returns the total number of bits (N) |
b.any() |
returns true if at least one bit is set |
b.none() |
returns true if no bits are set |
b.all() |
returns true if all bits are set |