USACO 2022 January Contest, Silver

Problem 1. Searching for soulmates 

Farmer John's cows each want to find their soulmate -- another cow with similar characteristics with whom they are maximally compatible. Each cow's personality is described by an integer pipi (1≤pi≤1018). Two cows with the same personality are soulmates. A cow can change her personality via a "change operation" by multiplying by 2, dividing by 2 (if pi is even), or adding 1.

Farmer John initially pairs his cows up in an arbitrary way. He is curious how many change operations would be needed to make each pair of cows into soulmates. For each pairing, decide the minimum number of change operations the first cow in the pair must make to become soulmates with the second cow.

INPUT FORMAT (input arrives from the terminal / stdin):

The first line contains N (1≤N≤10), the number of pairs of cows. Each of the remaining N lines describes a pair of cows in terms of two integers giving their personalities. The first number indicates the personality of the cow that must be changed to match the second.

OUTPUT FORMAT (print output to the terminal / stdout):

Please write N lines of output. For each pair, print the minimum number of operations required for the first cow to make her personality match that of the second.

SAMPLE INPUT:

6
31 13
12 8
25 6
10 24
1 1
997 120

SAMPLE OUTPUT:

8
3
8
3
0
20

For the first test case, an optimal sequence of changes is 31⟹32⟹16⟹8⟹9⟹10⟹11⟹12⟹1331⟹32⟹16⟹8⟹9⟹10⟹11⟹12⟹13.

For the second test case, an optimal sequence of changes is 12⟹6⟹7⟹812⟹6⟹7⟹8.

SCORING:

  • Test cases 1-4 satisfy pi≤10.
  • Test cases 5-12 satisfy no additional constraints.

题解

假设我们已经找到所有数字 x,使得我们需要恰好 k 个操作将 A 变成 x,并且假设我们已经对所有 k'≤ k 完成了这个操作。然后,我们可以找到所有数字 y,这样我们需要恰好 k+1 次操作才能从 A 到达 y,因为在应用除法/乘法/加法操作之后,这些 y 中的每一个都必须是先前的 x 值。此外,这些 y 中的每一个都应该是使用 k 或更少操作无法达到的数字。

#include<cstdio>
#include<vector>
#include<map> 
#include<algorithm>

using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
const ll tmp = 1e6 + 10;
map<ll, bool> mpp;
vector<int> vc[N];
int sum[N], a[N], mp[N], b[N], c[N], mp2[N];

int main() {
	int n, m, u, v;
	scanf("%d%d", &n, &m);
	for(int i = 0; i <= n; i++) sum[i] = 1, a[i] = i, mpp[tmp * i + i] = 1, vc[i].push_back(i);
	for(int i = 0; i < m ;i++) {
		scanf("%d%d", &u, &v);
		if(!mpp[tmp * u+ a[v]]) {
			mpp[tmp * u + a[v]] = 1; sum[a[v]]++, vc[a[v]].push_back(u);
		}
		if(!mpp[tmp * v + a[u]]) {
			mpp[tmp * v + a[u]] = 1; sum[a[u]]++, vc[a[u]].push_back(v);
		}
		int t = a[u];
		a[u] = a[v];
		a[v] = t;
	}
	for(int i = 0; i <= n; i++) {
		b[a[i]] = i;
		mp[i] = 1;
	}
	for(int i = 1; i <= n; i++) {
		if(!mp[i]) continue;
		mp[i] = 0;
		int t = b[i];
		int maxx = sum[i];
		int s = 1;
		int ccnt = 0;
		for(int j = 0; j < vc[i].size(); j++) {
			c[ccnt++] = vc[i][j];
			mp2[vc[i][j]] = 1;
		}
		while(t != i) {
			mp[t] = 0;
			for(int j = 0; j < vc[t].size(); j++) {
				if(!mp2[vc[t][j]]) {
					c[ccnt++] = vc[t][j];
					mp2[vc[t][j]] = 1;
				}
			}
			t = b[t];
		}
		for(int i = 0; i < ccnt; i++) mp2[c[i]] = 0;
		t = b[i];
		sum[i] = ccnt;
		while(t != i) {
			sum[t] = ccnt;
			t = b[t];
		}
	}
	for(int i = 1; i <= n; i++) {
		printf("%d\n", sum[i]);
	}
	return 0;
}

Problem 2. Cow Frisbee

Farmer John's NN cows (N≤3×105)N≤3×105) have heights 1,2,…,N1,2,…,N. One day, the cows are standing in a line in some order playing frisbee; let h1…hNh1…hN denote the heights of the cows in this order (so the hh's are a permutation of 1…N1…N).

Two cows at positions ii and jj in the line can successfully throw the frisbee back and forth if and only if every cow between them has height lower than min(hi,hj)min(hi,hj).

Please compute the sum of distances between all pairs of locations i<ji<j at which there resides a pair of cows that can successfully throw the frisbee back and forth. The distance between locations ii and jj is j−i+1j−i+1.

INPUT FORMAT (input arrives from the terminal / stdin):

The first line of input contains a single integer NN. The next line of input contains h1…hNh1…hN, separated by spaces.

OUTPUT FORMAT (print output to the terminal / stdout):

Output the sum of distances of all pairs of locations at which there are cows that can throw the frisbee back and forth. Note that the large size of integers involved in this problem may require the use of 64-bit integer data types (e.g., a "long long" in C/C++).

SAMPLE INPUT:

7
4 3 1 2 5 6 7

SAMPLE OUTPUT:

24

The pairs of successful locations in this example are as follows:

(1, 2), (1, 5), (2, 3), (2, 4), (2, 5), (3, 4), (4, 5), (5, 6), (6, 7)

SCORING

  • Test cases 1-3 satisfy N≤5000N≤5000.
  • Test cases 4-11 satisfy no additional constraints.

题解

我们可以遍历所有对 (i,j) 并检查它们是否满足给定条件。天真地这会花费 O(N3) 时间(有 O(N2) 时间,每个都需要 O(N) 时间来检查),但是通过检查所有对 (i,j) 的固定值很容易加快速度我在 O(N) 时间内。

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

int64_t ans = 0;
int N;
 
void add_contribution(const vector<int>& h) {
	vector<int> with_h(N+1);
	for (int i = 0; i < N; ++i) with_h[h[i]] = i;
	set<int> present;
	for (int cur_h = N; cur_h; --cur_h) {
		auto it = present.insert(with_h[cur_h]).first;
		if (next(it) != end(present)) ans += *next(it)-*it+1;
	} // the cow at position with_h[cur_h] can throw to the next cow after it
}

// either of the next two functions may be substituted in place of the first function

// using a linked list
void add_contribution_ll(const vector<int>& h) {
	vector<int> with_h(N+1);
	for (int i = 0; i < N; ++i) with_h[h[i]] = i;
	vector<int> pre(N), nex(N);
	for (int i = 0; i < N; ++i) {
		pre[i] = i-1;
		nex[i] = i+1;
	}
	for (int cur_h = 1; cur_h <= N; ++cur_h) {
		int pos = with_h[cur_h];
		int p = pre[pos], n = nex[pos];
		if (n != N) ans += n-pos+1, pre[n] = p;
		if (p != -1) nex[p] = n;
	}
}

// using a monotonic stack
void add_contribution_alt(const vector<int>& h) {
	stack<int> stk;
	for (int i = N-1; i >= 0; --i) {
		while (!stk.empty() && h[stk.top()] < h[i]) stk.pop();
		if (!stk.empty()) ans += stk.top()-i+1;
		stk.push(i);
	}
}

int main() { 
	cin >> N;
	vector<int> h(N); for (int& i: h) cin >> i;
	add_contribution(h);
	reverse(begin(h), end(h));
	add_contribution(h);
	cout << ans << "\n";
}

Problem 3. Cereal 2

Farmer John's cows like nothing more than cereal for breakfast! In fact, the cows have such large appetites that they will each eat an entire box of cereal for a single meal.

The farm has recently received a shipment with MM different types of cereal (2≤M≤105)(2≤M≤105). Unfortunately, there is only one box of each cereal! Each of the NN cows (1≤N≤105)(1≤N≤105) has a favorite cereal and a second favorite cereal. When given a selection of cereals to choose from, a cow performs the following process:

  1. If the box of her favorite cereal is still available, take it and leave.
  2. Otherwise, if the box of her second-favorite cereal is still available, take it and leave.
  3. Otherwise, she will moo with disappointment and leave without taking any cereal.

Find the minimum number of cows that go hungry if you permute them optimally. Also, find any permutation of the NN cows that achieves this minimum.

INPUT FORMAT (input arrives from the terminal / stdin):

The first line contains two space-separated integers NN and M.M.

For each 1≤i≤N,1≤i≤N, the ii-th line contains two space-separated integers fifi and sisi (1≤fi,si≤M1≤fi,si≤M and fi≠sifi≠si) denoting the favorite and second-favorite cereals of the ii-th cow.

OUTPUT FORMAT (print output to the terminal / stdout):

Print the minimum number of cows that go hungry, followed by any permutation of 1…N1…N that achieves this minimum. If there are multiple permutations, any one will be accepted.

SAMPLE INPUT:

8 10
2 1
3 4
2 3
6 5
7 8
6 7
7 5
5 8

SAMPLE OUTPUT:

1
1
3
2
8
4
6
5
7

In this example, there are 88 cows and 1010 types of cereal.

Note that we can effectively solve for the first three cows independently of the last five, since they share no favorite cereals in common.

If the first three cows choose in the order [1,2,3][1,2,3], then cow 11 will choose cereal 22, cow 22 will choose cereal 33, and cow 33 will go hungry.

If the first three cows choose in the order [1,3,2][1,3,2], then cow 11 will choose cereal 22, cow 33 will choose cereal 33, and cow 22 will choose cereal 44; none of these cows will go hungry.

Of course, there are other permutations that result in none of the first three cows going hungry. For example, if the first three cows choose in the order [3,1,2][3,1,2] then cow 33 will choose cereal 22, cow 11 will choose cereal 11, and cow 22 will choose cereal 33; again, none of cows [1,2,3][1,2,3] will go hungry.

It can be shown that out of the last five cows, at least one must go hungry.

SCORING:

  • In 44 out of 1414 test cases, N,M≤100N,M≤100.

题解

考虑 E>V 的情况。通过深度优先搜索,我们可以在这个组件中找到一棵树,并且有 E+1−V 条额外的边。将除了其中一头奶牛之外的所有奶牛都放在排序的末尾;我们不在乎他们是否挨饿。现在我们已经减少到只有 V 边的情况,并且我们知道在这种情况下我们可以找到没有奶牛挨饿的排序。所以总的来说,最多 E−V 头奶牛饿了(事实上,正好是 E−V 头奶牛:排序中的最后 E−V 头奶牛)。

#include <bits/stdc++.h>
 
using namespace std;
 
struct edge {
    int cow; // which cow's choice 
    int to;
    bool is_first;
 
    edge() {};
    edge(int cow, int to, bool is_first) : cow(cow), to(to), is_first(is_first) {};
};
 
int N, M;
 
vector<edge> adj[100001];
bool visited_cycle[100001]; // array for cycle finding
bool visited[100001]; // visited array for finding which order of cows we should use
bool gets_cereal[100001]; 
 
int hungry_cows = 0;
queue<int> order;
int ignore_edge = -1;
int first_cereal = -1; // the cereal we start the search from, if the CC is not a tree then this must be on a cycle
 
void find_cycle(int cur, int prev = -1) {
    visited_cycle[cur] = true; 
 
    for (edge next : adj[cur]) {
        if (visited_cycle[next.to]) {
            if (first_cereal == -1 && next.to != prev) {
                if (next.is_first) {
                    first_cereal = next.to; 
                } else {
                    first_cereal = cur;
                }
                
                ignore_edge = next.cow; 
                order.push(next.cow);
                gets_cereal[next.cow] = true;
            }
        } else {
            find_cycle(next.to, cur);
        }
    }
}
 
 
void dfs(int cur) {
    visited[cur] = true;
    for (edge next : adj[cur]) {
        if (!visited[next.to] && next.cow != ignore_edge) { 
            gets_cereal[next.cow] = true;
            order.push(next.cow);
            dfs(next.to);
        }
    }
}
 
 
int main() {
    cin >> N >> M;
    for (int i = 0; i < N; ++i) {
        int a, b;
        cin >> a >> b;
        adj[a].push_back(edge(i+1, b, false));
        adj[b].push_back(edge(i+1, a, true));
    }
 
    for (int i = 1; i <= M; ++i) {
        first_cereal = -1;
        ignore_edge = -1;
        if (!visited[i]) {
            find_cycle(i);
           
            if (first_cereal > 0) {
                dfs(first_cereal);
            } else {
                dfs(i);
            }
        }
    }
 
    for (int i = 1; i <= N; ++i) {
        if (!gets_cereal[i]) {
            ++hungry_cows;
            order.push(i);
        } 
    }
 
    cout << hungry_cows << endl;
    while (!order.empty()) {
        cout << order.front() << endl; 
        order.pop();
    }
 
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值