UVa – 11235 – Frequent values

Solution:

This problem can be efficiently solved by the Segment tree. 

Since it is stated that, the input will be strictly in increasing order, a natural technique can be applied.

For this we require 3 arrays :
1. array input stores the input numbers,
2. array count stores the frequency of each input number,
3. and array start stores the index of the input list where a particular input number appeared first.

For example,
input = { -1, -1, 1, 1, 1, 1, 3, 10, 10, 10}
count = { 2, 2, 4, 4, 4, 1, 3, 3, 3 }
start = { 1, 1, 3, 3, 3, 7, 8, 8, 8 }

At first, a segment tree is constructed where each node will store the value of the maximum count (from the count array) of its respective range [ a,b ].
Let, the query range be [ i,j ].
Now 2 cases can occur The value at index i and j are –
1. same  i.e input[ i ] = input[ j ].
2. different  i.e  input[ i ] ≠ input[ j ].

#Case 1:
Solving this case is the easiest. Since input[ i ] = input[ j ] , all the numbers in the range [ i,j ] are same ( since the numbers are non-descending ). So the answer for this case 1 is j – i + 1.

#Case 2:
In this case, there exists an index x where input[ i ] = input[ x ] and input[ i ] ≠ input[ x + 1 ]. Let, k = x + 1. So, the value of k = start [ i ] + count [ i ] .
So, the frequency of the value input[ i ] in the range [ i,k ] is cnt1 = k – i .
The frequency of input[ j ] in the range [ i,j ] is cnt2 = j –start [ j ] + 1 .

The maximum frequency of the values in range [ i,j ] may also exists in the range [ k, start[ j ] – 1 ]. This can be found by querying the segment tree for the maximum value in the range [ k, start[ j ] – 1 ]. Let the maximum count returned by the query be cnt3.

Therefore the answer for case 2 is max ( cnt1, cnt2 , cnt3 ).

 

#include <bits/stdc++.h>

#define pf                  printf
#define sf(a)               scanf("%d",&a)
#define sfl(a)              scanf("%lld",&a)
#define sff(a,b)            scanf("%d %d",&a,&b)
#define sffl(a,b)           scanf("%lld %lld",&a,&b)
#define sfff(a,b,c)         scanf("%d %d %d",&a,&b,&c)
#define sfffl(a,b,c)        scanf("%lld %lld %lld",&a,&b,&c)
#define sffff(a,b,c,d)      scanf("%d %d %d %d",&a,&b,&c,&d)
#define sffffl(a,b,c,d)     scanf("%lld %lld %lld %lld",&a,&b,&c,&d)
#define sfffff(a,b,c,d,e)   scanf("%d %d %d %d %d",&a,&b,&c,&d,&e)
#define sfffffl(a,b,c,d,e)  scanf("%lld %lld %lld %lld %lld",&a,&b,&c,&d,&e)
#define sfc(a)              scanf("%c",&a)
#define pii                 pair<int,int>
#define ms(a,b)             memset(a,b,sizeof(a))
#define pb(a)               push_back(a)
#define pbp(a,b)            push_back({a,b})
#define db                  double
#define ft                  float
#define ll                  long long
#define ull                 unsigned long long
#define pii                 pair<int,int>
#define ff                  first
#define ss                  second
#define sz(x)               x.size()
#define all(x)              x.begin(),x.end()
#define CIN                 ios_base::sync_with_stdio(0); cin.tie(0)
#define max3(a, b, c)       max(a, b) > max(b, c) ? max(a, b) : max(b, c)
#define min3(a, b, c)       min(a, b) < min(b, c) ? min(a, b) : min(b, c)
#define for0(i,n)          for(int i=0;i<n;i++)
#define for1(i,n)          for(int i=1;i<=n;i++)
#define forrev(i,n)        for(int i=n-1; i>=0; i--)
#define forab(i,a,b)       for(int i=a;i<=b;i++)
#define forba(i,b,a)       for(int i=b;i>=a;i--)
#define stlloop(x)          for(__typeof(x.begin()) it=x.begin();it!=x.end();it++)
#define gcd(a, b)           __gcd(a, b)
#define lcm(a, b)           ((a)*((b)/gcd(a,b)))
#define case1(z)            cout<<"Case "<<z<<": "
#define case2(z)            printf("Case %d: ",z)
#define PI                  acos(-1) //3.14159265358979323846264338328
#define valid(tx,ty)        tx>=0 && tx<row && ty>=0 && ty<col
#define intlim              2147483648
#define mx                  100005
#define inf                 100000008

/*------------------------------Graph Moves----------------------------*/
//const int fx[]={+1,-1,+0,+0};
//const int fy[]={+0,+0,+1,-1};
//const int fx[]={+0,+0,+1,-1,-1,+1,-1,+1};   // Kings Move
//const int fy[]={-1,+1,+0,+0,+1,+1,-1,-1};  // Kings Move
//const int fx[]={-2, -2, -1, -1,  1,  1,  2,  2};  // Knights Move
//const int fy[]={-1,  1, -2,  2, -2,  2, -1,  1}; // Knights Move
/*---------------------------------------------------------------------*/

using namespace std;

int input[mx],tree[3*mx],cnt[mx],start[mx];
map<int,int> mp;

void build (int node,int low,int high)
{
    if(low==high)
    {
        tree[node]=cnt[low];
        return;
    }
    int mid=(low+high)/2;
    int left=2*node;
    int right=2*node+1;
    build(left,low,mid);
    build(right,mid+1,high);
    tree[node]=max(tree[left],tree[right]);
}

int query(int node,int low,int high,int qlow,int qhigh)
{
    if(low>qhigh || high<qlow) return -inf;
    else if(low>=qlow && high<=qhigh)
    {
        return tree[node];
    }
    int mid=(low+high)/2;
    int left=2*node;
    int right=2*node+1;
    int l=query(left,low,mid,qlow,qhigh);
    int r=query(right,mid+1,high,qlow,qhigh);
    return max(l,r);
}

int main()
{
    //CIN;
//    freopen("in.txt","r",stdin);
//    freopen("out.txt","w",stdout);
    int n,q;
    while(sf(n)==1 && n!=0)
    {
        sf(q);
        for1(i,n)
        {
            sf(input[i]);
            mp[input[i]]++;
        }
        int y=-inf,k;
        for1(i,n)
        {
            int x=mp[input[i]];
            cnt[i]=x;
            if(input[i]!=y)
            {
                k=i;
                y=input[i];
            }
            start[i]=k;
        }
        build(1,1,n);
        for1(i,q)
        {
            int qlow,qhigh,cnt1,cnt2,cnt3;
            sff(qlow,qhigh);
            if(input[qlow]!=input[qhigh])
            {
                int k=start[qlow]+cnt[qlow];
                cnt1=k-qlow;
                cnt2=qhigh-start[qhigh]+1;
                cnt3=query(1,1,n,k,start[qhigh]-1);
                pf("%d\n",max3(cnt1,cnt2,cnt3));
            }
            else pf("%d\n",qhigh-qlow+1);
        }
        ms(tree,0);
        ms(cnt,0);
        ms(start,0);
        mp.clear();
    }
    return 0;
}

Light OJ – 1112 – Curious Robin Hood

Problem Link:

http://lightoj.com/volume_showproblem.php?problem=1112

Solution:

This problem can be solved by both Segment tree and BIT

1. Code applying Segment Tree:

/// 1. By applying Segment Tree ///
#include <bits/stdc++.h>

#define pf                  printf
#define sf(a)               scanf("%d",&a)
#define sfl(a)              scanf("%lld",&a)
#define sff(a,b)            scanf("%d %d",&a,&b)
#define sffl(a,b)           scanf("%lld %lld",&a,&b)
#define sfff(a,b,c)         scanf("%d %d %d",&a,&b,&c)
#define sfffl(a,b,c)        scanf("%lld %lld %lld",&a,&b,&c)
#define sffff(a,b,c,d)      scanf("%d %d %d %d",&a,&b,&c,&d)
#define sffffl(a,b,c,d)     scanf("%lld %lld %lld %lld",&a,&b,&c,&d)
#define sfffff(a,b,c,d,e)   scanf("%d %d %d %d %d",&a,&b,&c,&d,&e)
#define sfffffl(a,b,c,d,e)  scanf("%lld %lld %lld %lld %lld",&a,&b,&c,&d,&e)
#define sfc(a)              scanf("%c",&a)
#define ms(a,b)             memset(a,b,sizeof(a))
#define pb(a)               push_back(a)
#define pbp(a,b)            push_back({a,b})
#define db                  double
#define ft                  float
#define ll                  long long
#define ull                 unsigned long long
#define ff                  first
#define ss                  second
#define sz(x)               x.size()
#define qu                  queue
#define pqu                 priority_queue
#define vc                  vector
#define vi                  vector<int>
#define vll                 vector<long long>
#define pii                 pair<int,int>
#define pis                 pair<int,string>
#define psi                 pair<string,int>
#define all(x)              x.begin(),x.end()
#define CIN                 ios_base::sync_with_stdio(0); cin.tie(0)
#define max3(a, b, c)       max(a, b) > max(b, c) ? max(a, b) : max(b, c)
#define min3(a, b, c)       min(a, b) < min(b, c) ? min(a, b) : min(b, c)
#define for0(i,n)          for(int i=0;i<n;i++)
#define for1(i,n)          for(int i=1;i<=n;i++)
#define forcmp(i,n)        for(int i=1;i<n;i++)
#define forrev(i,n)        for(int i=n-1; i>=0; i--)
#define forab(i,a,b)       for(int i=a;i<=b;i++)
#define forba(i,b,a)       for(int i=b;i>=a;i--)
#define stlloop(x)          for(__typeof(x.begin()) it=x.begin();it!=x.end();it++)
#define gcd(a, b)           __gcd(a, b)
#define lcm(a, b)           ((a)*((b)/gcd(a,b)))
#define case1(z)            cout<<"Case "<<z<<": "
#define case2(z)            printf("Case %d:\n",z)
#define PI                  acos(-1) //3.14159265358979323846264338328
#define valid(tx,ty)        tx>=0 && tx<row && ty>=0 && ty<col
#define intlim              2147483648
#define MAX                 1000000
#define inf                 100000000
#define mx                  100005

/*------------------------------Graph Moves----------------------------*/
//const int fx[]={+1,-1,+0,+0};
//const int fy[]={+0,+0,+1,-1};
//const int fx[]={+0,+0,+1,-1,-1,+1,-1,+1};   // Kings Move
//const int fy[]={-1,+1,+0,+0,+1,+1,-1,-1};  // Kings Move
//const int fx[]={-2, -2, -1, -1,  1,  1,  2,  2};  // Knights Move
//const int fy[]={-1,  1, -2,  2, -2,  2, -1,  1}; // Knights Move
/*---------------------------------------------------------------------*/

using namespace std;

ll input[mx],tree[3*mx];

void build(int node,int low,int high)
{
    if(low==high)
    {
        tree[node]=input[low];
        return;
    }
    int mid=(low+high)/2;
    int left=2*node;
    int right=2*node+1;
    build(left,low,mid);
    build(right,mid+1,high);
    tree[node]=tree[left]+tree[right];
}

ll query(int node,int low,int high,int qlow,int qhigh)
{
    if(low>qhigh || high<qlow) /// No overlap
        return 0;
    else if(low>=qlow && high<=qhigh) /// Total overlap
        return tree[node];
    int mid=(low+high)/2;
    int left=2*node;
    int right=2*node+1;
    ll l=query(left,low,mid,qlow,qhigh);
    ll r=query(right,mid+1,high,qlow,qhigh);
    return l+r;
}

void update(int node,int low,int high,int index,ll val)
{
    if(low>index || high<index) /// No overlap
        return;
    else if(low>=index && high<=index) ///Or, if(low==high) /// Total overlap
    {
        tree[node]+=val;
        return;
    }
    int mid=(low+high)/2;
    int left=2*node;
    int right=2*node+1;
    update(left,low,mid,index,val);
    update(right,mid+1,high,index,val);
    tree[node]=tree[left]+tree[right];
}


int main()
{
    //CIN;
//    freopen("in.txt","r",stdin);
//    freopen("out.txt","w",stdout);
    int t;
    sf(t);
    for1(z,t)
    {
        int n,q;
        sff(n,q);
        for1(i,n) sf(input[i]);
        build(1,1,n);
        case2(z);
        for1(i,q)
        {
            int mark,qlow,qhigh,val,index;
            sf(mark);
            if(mark==1)
            {
                sf(index);
                index++;
                int a=query(1,1,n,index,index);
                pf("%d\n",a);
                update(1, 1, n, index, -a);
            }
            else if(mark==2)
            {
                sff(index,val);
                index++;
                update(1, 1, n, index, val);
            }
            else
            {
                sff(qlow,qhigh);
                qlow++,qhigh++;
                pf("%lld\n",query(1, 1, n, qlow,qhigh));
            }
        }
    }
    return 0;
}

Code applying BIT:

/// 2. Applying BIT ///

#include <bits/stdc++.h>

#define pf                  printf
#define sf(a)               scanf("%d",&a)
#define sfl(a)              scanf("%lld",&a)
#define sff(a,b)            scanf("%d %d",&a,&b)
#define sffl(a,b)           scanf("%lld %lld",&a,&b)
#define sfff(a,b,c)         scanf("%d %d %d",&a,&b,&c)
#define sfffl(a,b,c)        scanf("%lld %lld %lld",&a,&b,&c)
#define sffff(a,b,c,d)      scanf("%d %d %d %d",&a,&b,&c,&d)
#define sffffl(a,b,c,d)     scanf("%lld %lld %lld %lld",&a,&b,&c,&d)
#define sfffff(a,b,c,d,e)   scanf("%d %d %d %d %d",&a,&b,&c,&d,&e)
#define sfffffl(a,b,c,d,e)  scanf("%lld %lld %lld %lld %lld",&a,&b,&c,&d,&e)
#define sfc(a)              scanf("%c",&a)
#define pii                 pair<int,int>
#define ms(a,b)             memset(a,b,sizeof(a))
#define pb(a)               push_back(a)
#define pbp(a,b)            push_back({a,b})
#define db                  double
#define ft                  float
#define ll                  long long
#define ull                 unsigned long long
#define pii                 pair<int,int>
#define ff                  first
#define ss                  second
#define sz(x)               x.size()
#define all(x)              x.begin(),x.end()
#define CIN                 ios_base::sync_with_stdio(0); cin.tie(0)
#define max3(a, b, c)       max(a, b) > max(b, c) ? max(a, b) : max(b, c)
#define min3(a, b, c)       min(a, b) < min(b, c) ? min(a, b) : min(b, c)
#define for0(i,n)           for(int i=0;i<n;i++)
#define for1(i,n)           for(int i=1;i<=n;i++)
#define forrev(i,n)         for(int i=n-1; i>=0; i--)
#define forab(i,a,b)        for(int i=a;i<=b;i++)
#define forba(i,b,a)        for(int i=b;i>=a;i--)
#define stlloop(x)          for(__typeof(x.begin()) it=x.begin();it!=x.end();it++)
#define gcd(a, b)           __gcd(a, b)
#define lcm(a, b)           ((a)*((b)/gcd(a,b)))
#define case1(z)            cout<<"Case "<<z<<": "
#define case2(z)            printf("Case %d:\n",z)
#define PI                  acos(-1) //3.14159265358979323846264338328
#define valid(tx,ty)        tx>=0 && tx<row && ty>=0 && ty<col
#define intlim              2147483648
#define MAX                 100005
#define inf                 100000008

/*------------------------------Graph Moves----------------------------*/
//const int fx[]={+1,-1,+0,+0};
//const int fy[]={+0,+0,+1,-1};
//const int fx[]={+0,+0,+1,-1,-1,+1,-1,+1};   // Kings Move
//const int fy[]={-1,+1,+0,+0,+1,+1,-1,-1};  // Kings Move
//const int fx[]={-2, -2, -1, -1,  1,  1,  2,  2};  // Knights Move
//const int fy[]={-1,  1, -2,  2, -2,  2, -1,  1}; // Knights Move
/*---------------------------------------------------------------------*/

using namespace std;

ll input[MAX],tree[MAX];

ll query(int idx)
{
    ll sum=0;
    while(idx>0)
    {
        sum+=tree[idx];
        idx-=idx & (-idx);
    }
    return sum;
}

void update(int idx,ll val,int n)
{
    while(idx<=n)
    {
        tree[idx]+=val;
        idx+=idx & (-idx);
    }
}

int main()
{
    //CIN;
//    freopen("in.txt","r",stdin);
//    freopen("out.txt","w",stdout);
    int t,x,y;
    sf(t);
    for1(z,t)
    {
        case2(z);
        int n,q,x,idx,qlow,qhigh;
        ll val;
        sff(n,q);
        for1(i,n)
        {
            sfl(input[i]);
            update(i,input[i],n);
        }
        for1(i,q)
        {
            sf(x);
            if(x==1)
            {
                sf(idx);
                idx++;
                pf("%lld\n",input[idx]);
                update(idx,-input[idx],n);
                input[idx]=0;
            }
            else if(x==2)
            {
                sf(idx);
                sfl(val);
                idx++;
                update(idx,val,n);
                input[idx]+=val;
            }
            else
            {
                sff(qlow,qhigh);
                qlow++;
                qhigh++;
                pf("%lld\n",query(qhigh)-query(qlow-1));
            }
        }
        ms(tree,0);
    }
    return 0;
}

Light OJ – 1072 – Calm Down

HINT :
This problem needs just simple trigonometric ratio. Think about this and if you can’t solve yet, then read further for the solution.
SOLUTION IDEA :
In the first figure, the circle contains 6 small circles . So the angle is (PI/6) . When the circle contains n small circles then the angle will be (PI/n) .

Now from trigonometry , we can write,

sin(PI/n) = r/(R-r)……………(1)
Now just by solving the equation (1) we get,
r = sin(PI/n)*(R-r)
r =( R*sin(PI/n)) – (r*sin(PI/n)
1 =(( R*sin(PI/n))/r) – sin(PI/n) [ dividing both sides by r ]
1 + sin(PI/n) = ( R*sin(PI/n))/r
r = ( R*sin(PI/n)) / (1 + sin(PI/n)) ……..(2)
Now, we have all the values of right side of equation (2). So just drop the values in (2) and get r !! 😀

 
#include <bits/stdc++.h>

#define pf                  printf
#define sf(a)               scanf("%d",&a)
#define sfl(a)              scanf("%lld",&a)
#define sff(a,b)            scanf("%d %d",&a,&b)
#define sffl(a,b)           scanf("%lld %lld",&a,&b)
#define sfff(a,b,c)         scanf("%d %d %d",&a,&b,&c)
#define sfffl(a,b,c)        scanf("%lld %lld %lld",&a,&b,&c)
#define sffff(a,b,c,d)      scanf("%d %d %d %d",&a,&b,&c,&d)
#define sffffl(a,b,c,d)     scanf("%lld %lld %lld %lld",&a,&b,&c,&d)
#define sfffff(a,b,c,d,e)   scanf("%d %d %d %d %d",&a,&b,&c,&d,&e)
#define sfffffl(a,b,c,d,e)  scanf("%lld %lld %lld %lld %lld",&a,&b,&c,&d,&e)
#define ms(a,b)             memset(a,b,sizeof(a))
#define pb(a)               push_back(a)
#define db                  double
#define ft                  float
#define ll                  long long
#define ull                 unsigned long long
#define ff                  first
#define ss                  second
#define sz(x)               x.size()
#define qu                  queue
#define pqu                 priority_queue
#define vc                  vector
#define vi                  vector<int>
#define vll                 vector<long long>
#define pii                 pair<int,int>
#define pis                 pair<int,string>
#define psi                 pair<string,int>
#define all(x)              x.begin(),x.end()
#define CIN                 ios_base::sync_with_stdio(0); cin.tie(0)
#define loop0(i,n)          for(int i=0;i<n;i++)
#define loop1(i,n)          for(int i=1;i<=n;i++)
#define loopab(a,b)         for(int i=a;i<=b;i++)
#define stlloop(x)          for(__typeof(x.begin()) it=x.begin();it!=x.end();it++)
#define gcd(a, b)           __gcd(a, b)
#define lcm(a, b)           ((a)*((b)/gcd(a,b)))
#define case1(z)            cout<<"Case "<<z<<": "
#define case2(z,x)          cout<<"Case "<<z<<": "<<x<<endl
#define PI                  acos(-1) //3.14159265358979323846264338328
#define valid(tx,ty)        tx>=0 && tx<r && ty>=0 && ty<c
#define intlim              2147483648
#define MAX                 2000
#define inf                 10000000


/*------------------------------Graph Moves----------------------------*/
//const int fx[]={+1,-1,+0,+0};
//const int fy[]={+0,+0,+1,-1};
//const int fx[]={+0,+0,+1,-1,-1,+1,-1,+1};   // Kings Move
//const int fy[]={-1,+1,+0,+0,+1,+1,-1,-1};  // Kings Move
//const int fx[]={-2, -2, -1, -1,  1,  1,  2,  2};  // Knights Move
//const int fy[]={-1,  1, -2,  2, -2,  2, -1,  1}; // Knights Move
/*---------------------------------------------------------------------*/

using namespace std;

int main()
{
    //CIN;
//    freopen("in.txt","r",stdin);
//    freopen("out.txt","w",stdout);
    int t;
    sf(t);
    loop1(z,t)
    {
        db R;
        int n;
        scanf("%lf %d",&R,&n);
        db angle=sin(PI/n);
        db r=(angle*R)/(1+angle);
        pf("Case %d: %.6f\n",z,r);
    }
    return 0;
}

UVa – 11747 – Heavy Cycle Edges

Problem Link: https://onlinejudge.org/external/117/11747.pdf

Note: This problem is a variation of the Minimum Spanning Tree(MST) and it gives a deeper understanding of Krushkal’s algorithm.

We know that we need to run the Union-Find algorithm in Krushkal’s and we run that up to (node – 2) edges. Because we just need to check – starting from any node, whether we can reach any node or not. For example: if there are 3 nodes in a graph – 1,2,3. Then having edges between 1-2-3 would be enough and will form an MST. No direct edge between 1-3 is required.

But to solve this problem, we’ll need to check up to (node – 1) edges, to check for a cycle. That’s the main trick!

Code:

#include <iostream>
#include <iomanip>
#include <fstream>
#include <algorithm>
#include <new>
#include <map>
#include <unordered_map>
#include <queue>
#include <vector>
#include <set>
#include <algorithm>
#include <climits>

using namespace std;

int Find(int x, vector<int> &root)
{
    if(root[x]==x) return x;
    return root[x]=Find(root[x], root);
}

void Union(int x, int y, vector<int> &root, vector<int> &ranking)
{
    int rootX=Find(x, root);
    int rootY=Find(y, root);
    if(rootX!=rootY)
    {
        if(ranking[rootX]>=ranking[rootY]) root[rootY]=rootX;
        else if(ranking[rootY]>=ranking[rootX]) root[rootX]=rootY;
        else
        {
            root[rootY]=rootX;
            ranking[rootX]++;
        }
    }
}

vector<int> Krushkals(vector<pair<int, pair<int, int> > > &edges, int node)
{
    sort(edges.begin(), edges.end());
    vector<int> root(node);
    vector<int> ranking(node);
    for(int i=0; i<node; i++)
    {
        root[i]=i;
        ranking[i]=1;
    }
    int edge_added=0;
    vector<int> ans;
    int maxi=INT_MIN;
    for(int i=0; i<edges.size() && edge_added<node; i++)
    {
        int x=edges[i].second.first;
        int y=edges[i].second.second;
        int cost=edges[i].first;
        if(Find(x, root)!=Find(y, root)) //if no cycle exists
        {
            Union(x,y,root, ranking);
            edge_added++;
            maxi=max(maxi, cost);
        }
        else
        {
            
            maxi=max(maxi, cost);
            ans.push_back(maxi);
            maxi=INT_MIN;
        }
    }
    return ans;

}

int main()
{
    int node,edge;
    while(cin>>node>>edge)
    {
        if(node==0 && edge==0) break;
        vector<pair<int, pair<int, int> > > edges;

        for(int i=0; i<edge; i++)
        {
            int x,y,cost;
            cin>>x>>y>>cost;
            edges.push_back({cost, {x, y}});
        }
        vector<int> ans=Krushkals(edges, node);
        if(ans.size()==0) cout<<"forest"<<endl;
        else
        {
            for(int i=0; i<ans.size()-1; i++) cout<<ans[i]<<" ";
            cout<<ans[ans.size()-1]<<endl;
        }
    }
    return 0;
}

UVa – 11631 – Dark Roads

Problem Link: https://onlinejudge.org/external/116/11631.pdf

Note: This problem is a simple implementation of the Minimum Spanning Tree(MST) with a very simple tweak. I applied Prim’s algorithm here, Krushkal’s algorithm can also be used.

Code:

#include <iostream>
#include <iomanip>
#include <fstream>
#include <algorithm>
#include <new>
#include <map>
#include <unordered_map>
#include <queue>
#include <vector>
#include <set>
#include <algorithm>


using namespace std;

int Prims(vector<vector<pair<int, int> > > &edges, int node)
{
    priority_queue<pair<int,int>, vector<pair<int,int> >, greater<pair<int,int> > >pq;
    int vis[200005]= {0};
    int ans=0;
    int nodes_added=0;
    pq.push({0,0});
    while(nodes_added<node)
    {
        int from=pq.top().second;
        int cost=pq.top().first;
        pq.pop();
        if(!vis[from])
        {
            nodes_added++;
            vis[from]=1; //added to MST
            ans+=cost;
            for(int i=0; i<edges[from].size(); i++)
            {
                pair<int,int> to=edges[from][i];
                pq.push(to);
            }
        }
    }
    return ans;
}

int main()
{
    int node, edge;
    while(cin>>node>>edge)
    {
        if(node==0 && edge==0) break;
        vector<vector<pair<int, int> > > edges(node);
        int total_cost = 0;
        for(int i=0; i<edge; i++) //Building adjacency graph
        {
            int x,y,cost;
            cin>>x>>y>>cost;
            edges[x].push_back({cost, y});
            edges[y].push_back({cost, x});
            total_cost+=cost;
        }
        int ans=Prims(edges, node);
        cout<<total_cost-ans<<endl;
    }
    return 0;
}

UVa – 10557 – xyzzy

Problem Link: https://onlinejudge.org/external/105/10557.pdf

Hints:

Here are some important understandings to pick up from the problem statement while attempting to solve the problem. I struggled to some extent with catching up on the problem due to these facts 😓

  1. Since the problem is directly related to energy consumption, we need to find the path with maximum cost(energy).
  2. Another important factor is, here cost is not the edge cost. It’s the node’s cost. Notice that, it’s mentioned in the problem statement – “The energy value of this room is added to the player’s energy.” So, the usual edge cost storage of Bellman-Ford will not be implemented in this problem. So to track each node’s cost(rather than edge cost), I have taken a separate 1D vector named room_energy.
  3. At any time while traveling, if the energy becomes <=0, and there is no other path, then immediately the result would be “hopeless” because it is not possible to go to another room with equal or less than 0 energy.
  4. If the destination node can’t be reached using each edge once with >0 energy, then check for a positive cycle with the help of which(traversing that cycle repeatedly -> increasing energy enough to reach the destination node with >0 energy) the destination node can be reached with >0 energy.
  5. While taking the edges as input, it would be wrong to assume that – “Always the last line of a particular test case would be 0 0. That is if the energy value for room i and the number of doorways leaving room i both are 0, then we should break the loop, that is the end of input taking for that case” – no, that’s not the case. Because any room i can have an energy value 0 and also no room can be reached from i. So even the first or second room can have 0 0 cases (0 energy, 0 rooms connected).

My solution idea and approach:

It’s evident that we’ll have to use Bellman-Ford here. So is only Bellman-Ford enough to solve this problem? 🤔 Let’s see.

We’ll have to start from node 1 and will have to reach node n. Also, after reaching the destination node n, the energy cannot be 0. Obviously, before reaching node n, energy can’t be 0, if so, the programs generate “hopeless” immediately.

Now, we’ll have to think, is node n connected to node 1? It’s very simple, only running Bellman-Ford will tell us that. How? While running Bellman-Ford, lastly we’ll return dis[n]. If the value is not updated from 0, that means that was not reached. Though the dis[n] can be 0 not just for the disconnectivity, but also due to total_energy being 0 after reaching node n. But that is not our concern how dis[n] is 0, as we’ll lastly check if dis[n] is not 0, then “winnable” should be printed.

Now, let’s consider the possibility of cycle/loop in the graph as a general Bellman-Ford would consider. In this problem, already it is evident that we would need to consider the positive cycles only. Because that can give us infinite energy(by repeatedly traversing the cycle) to reach the destination if and only if any node of the positive cycle is connected to the destination. Notice that, it is mentioned in the problem that we can repeat any path any number of times.

So two kinds of connectivity to check-

  1. Whether node 1 is connected to node n. It doesn’t matter if any other nodes are not connected. For example, for n=5, a graph can be 1-3-5 2-4. Assuming energies between 1-3-5 are all >0, the output to this input would be “winnable”. Now, even if node 1 and node n are connected, the output would be “hopeless” if the only path to reach from 1 to n would result in <=0 energy. So for that scenario, we’ll have to check for a positive cycle.
  2. If there is a positive cycle, whether any node of the cycle is connected to the destination node n. If that’s the case, then the output will definitely be “winnable”.

Now, the most intuitive idea can be –

  1. At first, we’ll have to check whether node 1 is connected and reachable to node n or not. If yes, then winnable
  2. If step 1 is NO, then we’ll have to look for positive cycles and then whether that cycle is connected to n. If yes, then winnable, otherwise hopeless.

If we think like this, then to address the first part, Bellman-Ford is enough by checking the value of dis[n]. For the 2nd part, we can detect a positive cycle inside the Bellman-Ford. But what about checking the connectivity of that cycle to the node n? One way could be – while checking the existence of the positive cycle inside Bellman-Ford, then store the nodes involved in the cycle in a set. And then checking for each node separately, whether those nodes are connected to the node n or not using DFS; that is to run the DFS code k th time if there are k nodes involved in the positive cycle. But this process seems much time-consuming, right? There might be a better way. 🤔

Let’s interchange the above 2 steps to solve the problem! 💡

  1. At first, we’ll mark the nodes which are reachable from the destination node n using DFS.
  2. Then we’ll use Bellman-Ford to check whether node 1 is connected to node n with >0 energy or not. Also simultaneously will look for positive cycles and then whether that cycle is connected to the destination node n. If any of the answers to these two simultaneous things is yes, then winnable, otherwise hopeless.

So in this approach, we are doing the same thing, just with less time-consuming than the previous approach 😊

Steps in a nutshell:

  1. While creating the adjacency list adj for the input graph, create another adjacency list named adj_reverse. We’ll need that graph to mark the nodes which are reachable from the destination node.
  2. Run DFS on adj_reverse to mark the nodes which are reachable from the destination node.
  3. Run Bellman-Ford. If the destination node n is reachable from node 1 with >0 energy (dis[n] will give the result) or if there is a positive cycle and any node of the positive cycle is already visited (which we got from running the DFS on adj_reverse) and we return true immediately, that means the output would be “winnable”. Otherwise (the return is not true and dis[n] is <=0) the output would be “hopeless”.

Code:

#include <bits/stdc++.h>

using namespace std;

void dfs(int u, vector<vector<int> > &adj_reverse, vector<bool> &vis)
{
    vis[u]=1; // storing the connected nodes to the source node
    for(int i=0; i<adj_reverse[u].size(); i++)
    {
        int v=adj_reverse[u][i];
        if(!vis[v]) dfs(v, adj_reverse, vis);
    }
}

int bellman_ford(int node, vector<vector<int> > &adj, vector<int> &room_energy, vector<bool> &vis)
{
    vector<int> dis(node+1, 0);
    dis[1]=100;

    for(int i=0; i<node; i++)
    {
        for(int u=1; u<=node; u++)
        {
            for(int k=0; k<adj[u].size(); k++)
            {
                int v=adj[u][k];
                int cost=room_energy[v];

                if(dis[u] && dis[v]<dis[u]+cost)
                {
                    dis[v]=dis[u]+cost;
                }
            }
        }
    }

    //For checking positive cycle
    for(int u=1; u<=node; u++)
    {
        for(int k=0; k<adj[u].size(); k++)
        {
            int v=adj[u][k];
            int cost=room_energy[v];

            if(dis[u] && dis[v]<dis[u]+cost)
            {
                //whether any node of the positive cycle is connected to the destination node or not
                if(vis[v]) return true;
            }
        }
    }
    return dis[node];
}

int main()
{
//    freopen("in.txt","r",stdin);
//    freopen("out.txt","w",stdout);

    int n;
    while(cin>>n && n!=-1)
    {
        vector<vector<int> > adj(n+1), adj_reverse(n+1);
        vector<int> room_energy(n+1);
        for(int i=1; i<=n; i++)
        {
            int cost, from=i, num;
            cin>>cost>>num;

            room_energy[i]=cost;

            for(int j=0; j<num; j++)
            {
                int to;
                cin>>to;
                adj[from].push_back(to);
                adj_reverse[to].push_back(from);
            }
        }

        vector<bool> vis(n+1, 0);
        dfs(n, adj_reverse, vis); //Marking the connected nodes to the destination node in vis[]

        int result=bellman_ford(n, adj, room_energy, vis); //checking for cycle and the reachability
        if(result) cout<<"winnable"<<endl;
        else cout<<"hopeless"<<endl;
    }

    return 0;
}

Bellman-Ford with Path Print

Note: This is the basic algorithm of Bellman-Ford in addition to the Parent vector and the path_print function.

#include <bits/stdc++.h>

#define pii pair<int,int>

using namespace std;

void path_print(int dst, vector<int> &parent)
{
    if(dst==-1) return;
    path_print(parent[dst], parent);
    cout<<dst<<" ";
}

vector<long long int> bellman_ford(int node, int edge, vector<pair<pii, int> > &adj,  vector<int> &parent)
{
    vector<long long int> dis(node, INT_MAX);
    dis[0]=0;
    parent[0]=-1;

    for(int i=0; i<node; i++)
    {
        for(int j=0; j<edge; j++)
        {
            int u=adj[j].first.first;
            int v=adj[j].first.second;
            int cost=adj[j].second;
            if(dis[u] != INT_MAX && dis[v] > dis[u]+cost)
            {
                dis[v]=dis[u]+cost;
                parent[v]=u;
            }
        }
    }

    ///Running the loop one more time to find out whether the graph contains any negative cycle loop or not

    //bool neg_cycle=0; //To detect whether any negative cycle present in the graph or not
    for(int j=0; j<edge; j++)
    {
        int u=adj[j].first.first;
        int v=adj[j].first.second;
        int cost=adj[j].second;
        if(dis[u] != INT_MAX && dis[v] > dis[u]+cost)
        {
            /*
            neg_cycle=1;
            break;
            */
            dis[v]=INT_MAX; //Marking the nodes which will keep decreasing infinitly due to negative weight cycle
        }
    }
    return dis;
}


int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);

    int node,edge;
    cout<<"Enter the number of nodes: ";
    cin>>node; //assuming 0<=node
    cout<<"Enter the number of edges: ";
    cin>>edge;

    vector<pair<pii, int> > adj;


    for(int i=0; i<edge; i++)
    {
        int from, to, cost;
        cin>>from>>to>>cost;
        adj.push_back({{from, to}, cost});
    }

    //Taking "long long int" instead of "int" because adding up costs can cause overflow of integer
    vector<int> parent(node);
    vector<long long int> dis=bellman_ford(node, edge, adj, parent);


    int q;
    cout<<"Enter how many queries you want to make: ";
    cin>>q;
    for(int i=0; i<q; i++)
    {
        int dst;
        cout<<"Enter the destination: ";
        cin>>dst;
        if(dis[dst]==INT_MAX)
        {
            cout<<"Due to some negative cycle, this destination can't be reached."<<endl;
        }
        else
        {
            cout<<"The lowest cost is: "<<dis[dst]<<endl;
            cout<<"And the path is: ";
            path_print(dst, parent);
        }
    }

    return 0;
}

Bellman-Ford

Code:

#include <bits/stdc++.h>

#define pii pair<int,int>

using namespace std;

vector<long long int> bellman_ford(int node, int edge, vector<pair<pii, int> > &adj)
{
    vector<long long int> dis(node, INT_MAX);
    dis[0]=0;

    for(int i=0; i<node; i++)
    {
        for(int j=0; j<edge; j++)
        {
            int u=adj[j].first.first;
            int v=adj[j].first.second;
            int cost=adj[j].second;
            if(dis[u] != INT_MAX && dis[v] > dis[u]+cost)
            {
                dis[v]=dis[u]+cost;
            }
        }
    }

    ///Running the loop one more time to find out whether the graph contains any negative cycle loop or not

    //bool neg_cycle=0; //To detect whether any negative cycle present in the graph or not
    for(int j=0; j<edge; j++)
    {
        int u=adj[j].first.first;
        int v=adj[j].first.second;
        int cost=adj[j].second;
        if(dis[u] != INT_MAX && dis[v] > dis[u]+cost)
        {
            /*
            neg_cycle=1;
            break;
            */
            dis[v]=INT_MAX; //Marking the nodes which will keep decreasing infinitly due to negative weight cycle
        }
    }
    return dis;
}


int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);

    int node,edge;
    cout<<"Enter the number of nodes: ";
    cin>>node; //assuming 0<=node
    cout<<"Enter the number of edges: ";
    cin>>edge;

    vector<pair<pii, int> > adj;


    for(int i=0; i<edge; i++)
    {
        int from, to, cost;
        cin>>from>>to>>cost;
        adj.push_back({{from, to}, cost});
    }

    //Taking "long long int" instead of "int" because adding up costs can cause overflow of integer
    vector<long long int> dis=bellman_ford(node, edge, adj);


    int q;
    cout<<"Enter how many queries you want to make: ";
    cin>>q;
    for(int i=0; i<q; i++)
    {
        int dst;
        cout<<"Enter the destination: ";
        cin>>dst;
        if(dis[dst]==INT_MAX)
        {
            cout<<"Due to some negative cycle, this destination can't be reached."<<endl;
        }
        else
        {
            cout<<dis[dst]<<endl;
        }
    }

    return 0;
}