Disjoint Set implementation in C++ - c++

I came across this problem in an Online contest and I'm trying to solve it using Disjoint Set Data-structure.
Problem Definition:
Bob visits a nuclear power plant during his school excursion. He observes that there are n nuclear rods in the plant and the initial efficiency of the nuclear rods is 1. After a period of time nuclear rods start fusing with each other and combine to form a group. This process reduces the efficiency of the nuclear rods to square root of the size of the group. Bob, being a curious student, wants to know the total efficiency of the nuclear plant after some time. This is obtained by adding the efficiencies of the groups.
Initially all the rods belong to its own group of size 1. There are f fusions. If rod1 and rod2 get fused, it means their groups got fused.
Sample Input:
5 2
1 2
2 3
Sample Output:
3.73
Explanation:
n=5 fusions=2
group 1,2,3 => 1.73 (sqrt(3))
group 4 => 1
group 5 => 1
total = (1.73 + 1 + 1) = 3.73
My code:
#include <iostream>
#include <set>
#include <vector>
#include <stdio.h>
#include <math.h>
#include <iomanip>
using namespace std;
typedef long long int lli;
vector<lli> p,rank1,setSize; /* p keeps track of the parent
* rank1 keeps track of the rank
* setSize keeps track of the size of the set.
*/
lli findSet(lli i) { return (p[i] == i) ? i : (p[i] = findSet(p[i])); }
bool sameSet(lli x,lli y) { return findSet(x) == findSet(y); }
void union1(lli x,lli y) { // union merges two sets.
if(!sameSet(x,y)) {
lli i = findSet(x), j = findSet(y);
if(rank1[i] > rank1[j]) {
p[j] = i;
setSize[i] += setSize[j];
}
else {
p[i] = j;
setSize[j] += setSize[i];
if(rank1[i] == rank1[j])
rank1[j]++;
}
}
}
int main() {
freopen("input","r",stdin);
lli n;
cin >> n; //number of nuclear rods
setSize.assign(n,1); //Initialize the setSize with 1 because every element is in its own set
p.assign(n,0);
rank1.assign(n,0); //Initialize ranks with 0's.
for(lli i = 0; i < n; i++) p[i] = i; //Every set is distinct. Thus it is its own parent.
lli f;
cin >> f; //Number of fusions.
while(f--){
lli x,y;
cin >> x >> y; //combine two rods
union1(x,y);
}
double ans;
set<lli> s (p.begin(),p.end()); //Get the representative of all the sets.
for(lli i : s){
ans += sqrt(setSize[i]); //sum the sqrt of all the members of that set.
}
printf("\n%.2f", ans); //display the answer in 2 decimal places.
}
The above code seems to work for all testcases but one.
The input is here for which my code fails.
The expected output is : 67484.82
My output : 67912.32
I can't really work out where I went wrong since the input is really huge.
Any help would really be appreciated. Thanks in advance.

p holds the immediate parents of the elements and not their findSet values. Hence when you do set<lli> s (p.begin(),p.end()); you can have additional elements there.
There are two ways I can think of to deal with this:
Insert findSet(i) into the set with a loop instead of directly putting p
After you do setSize[i] += setSize[j], set setSize[j] = 0. This way, the intermediate parents will not contribute to the sum.

Related

Find how much xtreme distance two people walk together

Amir and Bond are walking on a street. Initially, both are at the position X=0 and they start walking in the direction of increasing X. After N seconds, they stop. Let's denote Amir's speed and Bond's speed during the i-th of these seconds by Ai and Bi respectively.
Sometimes, Aman and Bond walk together, i.e. with the same speed side by side. Let's define the xtreme distance as the total distance they walk this way. Find this xtreme distance.
Input
The first line of the input contains a single integer T denoting the number of test cases. The description of T test cases follows.
The first line of each test case contains a single integer N.
The second line contains N space-separated integers A1,A2,…,AN.
The third line contains N space-separated integers B1,B2,…,BN.
Output
For each test case, print a single line containing one integer ― the total weird distance. It can be proved that this distance is an integer.
Constraints
1≤T≤20
1≤N≤10e5
1≤Ai≤10e5 for each valid i
1≤Bi≤10e5 for each valid i
the sum of N over all test cases does not exceed 10e6
Code
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
int main()
{
int T;
cin >> T;
while(T--)
{
long long int N;
long long int i, w = 0;
cin >> N;
int * A = new int [N+1];
int * X = new int [N+1];
int * B = new int [N+1];
int * Y = new int [N+1];
Y[0] = 0;
X[0] = 0;
for(i=1;i<=N;i++)
{
cin >> A[i];
X[i] = X[i-1] + A[i];
}
for(i=1;i<=N;i++)
{
cin >> B[i];
Y[i] = Y[i-1] + B[i];
}
for(i=1;i<=N;i++)
{
if((X[i]-X[i-1]) == (Y[i]-Y[i-1]))
w += (Y[i] - Y[i-1]);
}
cout << w << endl;
delete [] A;
delete [] B;
delete [] X;
delete [] Y;
}
return 0;
}
Example Input
3
4
1 3 3 4
1 2 4 4
2
2 3
3 2
2
3 3
3 3
Example Output
5
0
6
Error
I am not able to figure out the error (may be there is error in constraints)

Painting rows and columns of a 2D matrix

Problem: We have given a nxm matrix, initially initialized with 0. We have to perform k queries:
Each query supports one of the two operations.
paint all elements in ri row with the colour ai .
paint all elements in ci column with the colour ai.
The same element can be painted more than once. But the color of that element is the same as the last painted colour for that element. You have to print the final matrix after painting.
Input: The first line contains three space-separated integers N,M,K
Next K lines consist of exactly one typep of operation to be performed
1) 1 ri ai means row ri is painted with color ai
2) 2 ci ai means column ci is painted with color ai
Output: Print the final matrix of size nxm after painting.
Sample Input: 3 3 3
1 1 3 2 2 1 1 2 2 Output: 3 1 3 2 2 2 0 1 0
I have written the following code to solve it but it is showing TLE for some test cases. Can you give me some idea how to solve it in efficient way?
My Code
#include<bits/stdc++.h>
#define ll long long int
using namespace std;
int mat[5000][5000];
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(0);
int n,m,k,q,r,c,val,i,j,re;
cin>>n>>m>>re;
while(re--)
{
cin>>q;
if(q==1)
{
cin>>r;
cin>>val;
i=r-1;
for(j=0,k=m-1;j<=k;j++,k--)
{
mat[i][j]=val;
mat[i][k]=val;
}
}
else if(q==2)
{
cin>>c>>val;
j=c-1;
for(i=0,k=n-1;i<=k;i++,k--)
{
mat[i][j]=val;
mat[k][j]=val;
}
}
}
for(i=0;i<n;i++)
{
for(j=0;j<m;j++)
{
cout<<mat[i][j]<<" ";
}
cout<<endl;
}
}
It is only needed to memorize the last color that was affected to a given row or a given column, and the last time at which it was performed.
Then, for a given element mat[i][j], we simply have to check if the last modification on row i occured before of after the last modification on column j.
We don't even need to set such a matrix.
#include <iostream>
#include <ios>
#include <vector>
int main() {
std::ios_base::sync_with_stdio(false);
std::cin.tie(0);
int n, m, re;
std::cin >> n >> m >> re;
std::vector<int> row_color (n, 0), row_date (n, -1);
std::vector<int> col_color (m, 0), col_date (m, -1);
int date = 0;
while (re--) {
int q, index, val;
std::cin >> q >> index >> val;
index--;
if (q == 1) {
row_color[index] = val;
row_date[index] = date;
} else {
col_color[index] = val;
col_date[index] = date;
}
++date;
}
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
int val = (row_date[i] > col_date[j]) ? row_color[i] : col_color[j];
std::cout << val << " ";
}
std::cout << "\n";
}
}
Instead of performing all the paint operations as they come in, you could:
While parsing the input, keep and update:
For each row the last color ai it is supposed to be painted in and the corresponding value k (running from 0 to K)
The same for each column
Setup an array of paint operations that combines both row and column paintings for all rows, columns where a painting occured
Sort the array based on k
Perform these operations on a matrix initialized with 0
This algorithm has an advantage if there is a large k (so, lots of overpainting) which you could expect from these kind of problems.

Optimizing a recursive function

I'm creating a program that returns the least quantity of sums required to get to a number (n) using only 1, 2, 6 and 13. It works perfectly for small values of n, but once n gets to values like 200 it takes the program too much time to calculate the result.
Therefore, I have two questions:
1. Is there any way to make the recursion faster?
2. Should I avoid using recursion and use a loop instead?
Here's the commented code:
#include <iostream>
#define MAX 500000
using namespace std;
void cal(int inp, int &mini, int counter = 0);
int main (void)
{
//Gets input
int n;
cin >> n;
//Defines mini as the MAX result we can get
int mini = MAX;
//Calls the function
cal(n, mini);
//Prints the best result
cout << mini << endl;
return 0;
}
void cal(int inp, int &mini, int counter)
{
//Breaks recursion if it finds an answer
if(!inp)
{
if(counter<mini) mini = counter;
return;
}
//Breaks recursion if the input is negative
//or the counter is more than the best result
else if((inp<0) || (counter>mini)) return;
//Counts amount of recursions
counter++;
//Tries every combination
cal(inp-13, mini, counter);
cal(inp-6, mini, counter);
cal(inp-2, mini, counter);
cal(inp-1, mini, counter);
return;
}
Thank you
The problem is your brute force. Let me suggest something better:
Preliminaries: If you have two 1s, it is always better to use a 2. If you have three 2s, it is better to use a 6. If you have thirteen 6s, it is better to use six thirteens.
So the any admissable sum will always look like n = 13m+k where k is written as a sum of 1, 2, and 6. With the preliminaries, we know that for the optimal sum k will never exceed 1+2*2+12*6 = 77. (The reverse doesn't hold. Not any number below 78 is best written without 13s of course.) So brute forcing those is good enough. You can then use a lookup table.
This could still be optimized further, but it should not break down at 200.
Assuming you have found your first 77 entries (which can be optimized as well) you can do this (still unoptimized ;-):
int num_13 = ((n-78) / 13) + 1;
int sum_length = MAX;
for (i = num_13; i*13 < n; i++) {
int tmp = entries_77[n-i*13]+i;
if (tmp < sum_length) {
num_13 = i;
sum_length = tmp;
}
}
I would be even quicker to compile an array for the equivalence classes modulo 13, since for any given equivalence class any number exceeding 78 will have the same k.
You can use DP (Dynamic Programming) approach to solve your problem. It's well known Coins Problem
Your recursion needs a memoization to avoid repetitive calculation. And no need for the second and third parameter of the recursion. I have updated and put explanation on your code. Let me know if you have any confusion.
#include <iostream>
#include <string.h>
#define INF 999999
using namespace std;
int cal(int inp);
int mem[502];
int main (void)
{
//Gets input
int n;
cin >> n;
//initialzing the array for using with memoization
memset(mem,-1,sizeof(mem));
//Calls the function
//Prints the best result
cout << cal(n) << endl;
return 0;
}
//returns the minimum quantity of sum operations to get inp.
int cal(int inp)
{
//Breaks recursion if it finds an answer.
//Return cost 0. As in this stage no processing was done.
if(!inp)
return 0;
// Returning infinite cost for invalid case.
if(inp < 0)
return INF;
int _ret = mem[inp];
// If already visited here before then no need to calcuate again.
// Just return previous calculation. This is called memoisation.
// If not visited then _ret would have equal to -1.
if(_ret >=0 )
return _ret;
_ret = INF;
//Tries every combination and takes the minimum cost.
_ret = min(_ret, cal(inp-13)+1);
_ret = min(_ret,cal(inp-6)+1);
_ret = min(_ret,cal(inp-2)+1);
_ret = min(_ret,cal(inp-1)+1);
// Updating the value so that can be used for memoization.
mem[inp] = _ret;
return _ret;
}
This will also work for larger numbers. Complexity is 4*n.

How do I solve this using union-find algorithm?

Question: http://codeforces.com/contest/468/problem/B
Little X has n distinct integers: p1, p2, ..., pn. He wants to divide all of them into two sets A and B. The following two conditions must be satisfied:
If number x belongs to set A, then number a - x must also belong to set A.
If number x belongs to set B, then number b - x must also belong to set B.
Help Little X divide the numbers into two sets or determine that it's impossible.
Input
The first line contains three space-separated integers n, a, b (1 ≤ n ≤ 105; 1 ≤ a, b ≤ 109). The next line contains n space-separated distinct integers p1, p2, ..., pn (1 ≤ pi ≤ 109).
Output
If there is a way to divide the numbers into two sets, then print "YES" in the first line. Then print n integers: b1, b2, ..., bn (bi equals either 0, or 1), describing the division. If bi equals to 0, then pi belongs to set A, otherwise it belongs to set B.
If it's impossible, print "NO" (without the quotes).
Now, I developed the following code:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
#include <map>
using namespace std;
int n,a,b,id;
int p[100100];
set<int> st;
map<int,int> mp;
int fa[100100];
int find(int x)
{
if(x==fa[x]) return x;
return fa[x]=find(fa[x]);
}
void Bing(int a,int b)
{
int A=find(a),B=find(b);
if(A==B) return ;
fa[B]=A;
}
int main()
{
scanf("%d%d%d",&n,&a,&b);
for(int i=1;i<=n;i++)
{
scanf("%d",p+i);
st.insert(p[i]);
mp[p[i]]=++id;
fa[i]=i;
}
fa[n+1]=n+1;///A
fa[n+2]=n+2;///B
for(int i=1;i<=n;i++)
{
int x=p[i];
int flag = 0;
if(st.count(a-x))
{
Bing(mp[x],mp[a-x]);
flag = 1;
}
else
{
Bing(n+1,mp[x]);
flag = 1;
}
if(st.count(b-x) && flag == 0)
{
Bing(mp[x],mp[b-x]);
}
else if (flag == 0)
{
Bing(n+2,mp[x]);
}
}
if(find(n+1)==find(n+2))
{
puts("NO");
}
else
{
puts("YES");
for(int i=1;i<=n;i++)
{
printf("%d ",find(i)==find(n+1));
}
putchar(10);
}
return 0;
}
Basically, try to merge every element with either in Set A or in Set B. And then finally output NO if both of them are merged in turn. However, this gives a wrong answer on input as:
3 3 4
1 2 4
Output should be: NO whereas my code gives output as
YES
0 0 1
Where am I going wrong in my logic? Please help!
Consider the following cases for P being the set of all input numbers p[i]:
There is a number n1 in P that satisfies n1 = a - p[i] but no number n2 in P that satisfies n2 = b - p[i]
There is a number n1 in P that satisfies n1 = b - p[i] but no number n2 in P that satisfies n2 = a - p[i]
There is no number n in P that satisfies n = a - p[i] OR n = b - p[i]
There is a number n1 in P that satisfies n1 = a - p[i] AND a number n2 in P that satisfies n2 = b - p[i]
Whatever else may happen, if you run into situation 3. or 4. you want to report a failure ("NO").
If all numbers p[i] belong to case 1. or 2. the result should be valid.
You should check your code line by line, if it is compatible with the provided cases here.

How to optimise this code Algorithmically? Number of ways to assign N subjects to N students?

Your task will be to calculate number of different assignments of n
different topics to n students such that everybody gets exactly one
topic he likes.
Each test case begins with number of students n (1<=n<=20). Each of the next n lines contains n integers describing preferences of one student. 1 at the ith position means that this student likes the ith topic, 0 means that he definitely doesn't want to take it.
I was solving this by defining DP[i][mask] to represent the number of ways to form mask set using i elements only!
Here mask is a subset of Subjects which shows me how many and which subjects are taken.
The recurrence being
for(i=1;i<N;i++) //Student
for(j=1;j<(1<<N);j++) //Subject Subset
{
for(k=0;k<N;k++) //Selecting subject
if( (j&(1<<k)) && A[i][k] )
DP[i][j]+=DP[i-1][j^(1<<k)];
}
i.e. Taking one subject from the i th student's favourite subject and recursing for the lower states!!
However, this is not enough as the complexity of the solution is O(2^N * N^2).
We need to bring down one N at least!
How do I reduce the complexity of this problem? Here is my Code:
#include<bits/stdc++.h>
using namespace std;
long long DP[20][(1<<20)+1];
int main()
{
int T;
scanf("%d",&T);
for(;T;--T)
{
int N,i,j,k;
scanf("%d",&N);
int A[N+1][N+1];
for(i=0;i<N;i++)
for(j=0;j<N;j++)
scanf("%d",&A[i][j]);
/*
First of all let's think about the state!!
DP[i][j] where i is the i th student I am considering j is a bitmask which tells me which all subjects are
Done!!
********All Right************
So what can the recurrence be..?
traverse over the array A[i][]
If We can use the k th element of i.e A[i][k].
We need to try assigning it and Get the number of ways
*********Seems Fine *********
What will be the base case??
When only one element left in the mask and i is 1 we won't traverse more down!!
**OK**
SO what is the topological order of DP states !>>>????
I dont Know!! Let's think... Let me explain ummmmmmmmmmmmmmmmmmmmmmmmmmmm
ummmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
I am like calling a smaller i with smaller subset!
for every i
go in the order of increasing subsets
I think that should work!! Let's see
*/
for(i=0;i<(1<<N);i++)
DP[0][i]=0;
for(i=0;i<N;i++)
if(A[0][i])
DP[0][1<<i]=1;
for(i=1;i<N;i++) //Student
for(j=1;j<(1<<N);j++) //Subject Subset
{
DP[i][j]=0;
for(k=0;k<N;k++) //Selecting subject
if( (j&(1<<k)) && A[i][k] )
DP[i][j]+=DP[i-1][j^(1<<k)];
}
long long ans=0;
for(i=1;i<(1<<N);i++)
ans+=DP[N-1][i];
printf("%lld\n",ans);
}
return 0;
}
Problem Link in case you need it: Spoj
There are two techniques you can use to reduce the time complexity:
Meet at the middle
Breath first search
So, we notice that, for first ith person, we don't need all n bit to be set, but just i bits to be set, so rather than iterating through all number from (0 to 2^n), we only need to iterating through all number that has i bits are set. We can do this by using BFS
Second, if we using one array dp to store the number of ways to assign subjects to first half n/2 people, and other array dp1 to store the number of ways to assign subjects to second half n/2 people, so the number of ways to assign subjects to all n people is
int x = a number that has n/2 bit set
int result = sum (dp[x] + dp1[2^n - 1 - x]);
Time complexity will be C(n, n/2)*n/2*n with n = 20 ~ 3*10^7 operations.
You can improve the speed (by a factor of 3.7 on my computer on the worst case input) with some bit hacking micro-optimisations on the inner loop.
The idea is that given a binary number such as 10100 you can extract a single set bit via the operation 10100 & -10100 = 00100.
We can therefore change the loop over k to only loop over important bits with this code:
#include<bits/stdc++.h>
using namespace std;
long long DP[20][(1<<20)+1];
int main()
{
int T;
scanf("%d",&T);
for(;T;--T)
{
int N,i,j,k;
int masks[20]; // ADDED
scanf("%d",&N);
int A[N+1][N+1];
for(i=0;i<N;i++) {
masks[i] = 0;
for(j=0;j<N;j++) {
scanf("%d",&A[i][j]);
masks[i] |= A[i][j]<<j; // ADDED
}
}
for(i=0;i<(1<<N);i++)
DP[0][i]=0;
for(i=0;i<N;i++)
if(A[0][i])
DP[0][1<<i]=1;
for(i=1;i<N;i++) //Student
for(j=1;j<(1<<N);j++) //Subject Subset
{
long long t = 0; // ADDED
int mask = j & masks[i]; // ADDED
while(mask) { // ADDED
int bit = mask & -mask; // ADDED
t += DP[i-1][j - bit]; // ADDED
mask -= bit; // ADDED
} // ADDED
DP[i][j]=t; // ADDED
}
long long ans=0;
for(i=1;i<(1<<N);i++)
ans+=DP[N-1][i];
printf("%lld\n",ans);
}
return 0;
}
Well ,
I figured it out that we can make the solution faster! This is how!!
We dont need to take care of the i loop ! Why??
If a mask contains i bits then only it can fetch us some ways to do else wise it is zero because one subject is to be assigned to each one!
So now , the recurrence changes to
for (Every bitmask)
Get the bit count of mask!(This is the only i associated with the mask). Each mask is associated with only one i due to above argument!!
Check in all ways we can achieve the current state by giving all the favourable subjects to the student! Now it will lead to a mask of lower bit numbers which is also associated with unique i , specifically i-1 !
So now , that is great we can have a One-D DP over the situation...
for(j=1;j<(1<<N);j++) //Subject Subset
{
DP[j]=0;
i=__builtin_popcount(j);
for(k=1;k<=N;k++) //Selecting subject
if( (j&(1<<(k-1))) && A[i][k] )
DP[j]+=DP[j^(1<<(k-1))];
}
My AC CODE(Run time 0.78) with scope of improvements by preprocessing the bit count instead of __builtin_popcount()
#include<bits/stdc++.h>
using namespace std;
long long DP[(1<<20)+1];
int main()
{
int T;
scanf("%d",&T);
for(;T;--T)
{
int N,i,j,k;
scanf("%d",&N);
int A[N+1][N+1];
for(i=1;i<=N;i++)
for(j=1;j<=N;j++)
scanf("%d",&A[i][j]);
for(i=0;i<(1<<N);i++)
DP[i]=0;
DP[0]=1;
for(j=1;j<(1<<N);j++) //Subject Subset
{
DP[j]=0;
i=__builtin_popcount(j);
for(k=1;k<=N;k++) //Selecting subject
if( (j&(1<<(k-1))) && A[i][k] )
DP[j]+=DP[j^(1<<(k-1))];
}
long long ans=0;
printf("%lld\n",DP[(1<<N)-1]);
}
return 0;
}
In comparison to optimised code by #Peter. He was able to optimise it to pass the time limit Run Time 2.60 sec on test data by spoj!
#include<bits/stdc++.h>
using namespace std;
long long DP[20][(1<<20)+1];
int main()
{
int T;
scanf("%d",&T);
for(;T;--T)
{
int N,i,j,k;
int masks[20]; // ADDED
scanf("%d",&N);
int A[N+1][N+1];
for(i=0;i<N;i++) {
masks[i] = 0;
for(j=0;j<N;j++) {
scanf("%d",&A[i][j]);
masks[i] |= A[i][j]<<j; // ADDED
}
}
for(i=0;i<(1<<N);i++)
DP[0][i]=0;
for(i=0;i<N;i++)
if(A[0][i])
DP[0][1<<i]=1;
for(i=1;i<N;i++) //Student
for(j=1;j<(1<<N);j++) //Subject Subset
{
long long t = 0; // ADDED
int mask = j & masks[i]; // ADDED
while(mask) { // ADDED
int bit = mask & -mask; // ADDED
t += DP[i-1][j - bit]; // ADDED
mask -= bit; // ADDED
} // ADDED
DP[i][j]=t; // ADDED
}
long long ans=0;
for(i=1;i<(1<<N);i++)
ans+=DP[N-1][i];
printf("%lld\n",ans);
}
return 0;
}