Skip to content

USACO 2022 January Contest Silver Division - Cereal 2#

Problem link: here

Solution Author: Stefan Dascalescu

Problem Solution#

First, it's obvious that we want to make a graph out of the cows' preferences and have the cereals as vertexes and the cows as edges. Normally, this leads into having a directed graph and in order to find the answer, we could then find the SCCs.

Problem idea: we want to find the connected components of the graph. Initially the ideas I had were to find the SCCs and solve the problem like that. However, it is enough to consider the graph without the directions and then we have a few cases we need to deal with for each connected component:

  • the connected component is a tree, so we can choose a cereal for each cow
  • the connected component has a cycle, so we can first solve the problem for the cycle, find the extra edge which links the cycle and then solve the rest of the connected component as if it was a tree.

In the end, the algorithm obtained is linear and will work on every single test case, without needing SCCs.

Source code#

The source code in C++ can be seen below.

#include <bits/stdc++.h>
using namespace std;

struct edg
{
    int dest, dir, cnt; 
};
vector<vector<edg> > graph;

int hungry, first_cereal, xtr_edge;
bool vis[100002], viscycle[100002], winner[100002];
vector<int> ans;

// here we find the cycle and the edge we need to ignore
void dfscycle(int node, int prev = -1)
{
    viscycle[node] = 1;
    for(int i = 0; i < (int) graph[node].size(); i++)
    {
        if(viscycle[graph[node][i].dest])
        {
            if(first_cereal == -1 && graph[node][i].dest != prev)
            {
                if(graph[node][i].dir == 1)
                    first_cereal = graph[node][i].dest;
                else
                    first_cereal = node;
                xtr_edge = graph[node][i].cnt;
                ans.push_back(graph[node][i].cnt);
                winner[graph[node][i].cnt] = 1;
            }
        }
        else
            dfscycle(graph[node][i].dest, node);
    }
}

// here we solve the problem as if it's a tree
void dfs(int node)
{
    vis[node] = 1;
    for(int i = 0; i < (int) graph[node].size(); i++)
    {
        if(!vis[graph[node][i].dest] && graph[node][i].cnt != xtr_edge)
        {
            winner[graph[node][i].cnt] = 1;
            ans.push_back(graph[node][i].cnt);
            dfs(graph[node][i].dest);
        }
    }
}
int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int n ,m;
    cin >> n >> m;

    graph.resize(m+1);

    for(int i = 1; i <= n; i++)
    {
        int a, b;
        cin >> a >> b;
        graph[a].push_back({b, 0, i});
        graph[b].push_back({a, 1, i});
    }

    for(int i = 1; i <= m; i++)
    {
        first_cereal = -1;
        xtr_edge = -1;
        if(!vis[i])
        {
            dfscycle(i);
            if(first_cereal > 0)
                dfs(first_cereal);
            else
                dfs(i);
        }
    }

    for(int i = 1; i <= n; i++)
        if(!winner[i])
            hungry++, ans.push_back(i);

    cout << hungry << '\n';
    for(auto x : ans)
        cout << x << '\n';
    return 0;
}