USACO 2月 2021-2022 February Contest Silver银组 题解

要准备usaco的铁铁们可以参考这个文章哦!

想刷好USACO——看这篇文章就够了_GeekAlice的博客-CSDN博客usaco 必刷网站https://blog.csdn.net/GeekAlice/article/details/122291933

Problem 1. Redistributing Gifts

Farmer John has N gifts labeled 1…N for his N cows, also labeled 1…N(1≤N≤500). Each cow has a wishlist, which is a permutation of all N gifts such that the cow prefers gifts that appear earlier in the list over gifts that appear later in the list.

FJ was lazy and just assigned gift i to cow i for all i. Now, the cows have gathered amongst themselves and decided to reassign the gifts such that after reassignment, every cow ends up with the same gift as she did originally, or a gift that she prefers over the one she was originally assigned.

For each i from 1 to N, compute the most preferred gift cow i could hope to receive after reassignment.

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

The first line contains N. The next N lines each contain the preference list of a cow. It is guaranteed that each line forms a permutation of 1…N.

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

Please output NN lines, the ii-th of which contains the most preferred gift cow ii could hope to receive after reassignment.

SAMPLE INPUT:

4
1 2 3 4
1 3 2 4
1 2 3 4
1 2 3 4

SAMPLE OUTPUT:

1
3
2
4

In this example, there are two possible reassignments:

  • The original assignment: cow 1 receives gift 11, cow 22 receives gift 22, cow 33 receives gift 33, and cow 44 receives gift 44.
  • Cow 11 receives gift 11, cow 22 receives gift 33, cow 33 receives gift 22, and cow 44 receives gift 44.

Observe that both cows 11 and 44 cannot hope to receive better gifts than they were originally assigned. However, both cows 22 and 33 can.

SCORING:

  • Test cases 2-3 satisfy N≤8N≤8.
  • Test cases 4-11 satisfy no additional constraints.

        只有当奶牛 i 接收奶牛 j 的分布对应于将 G 的顶点划分为一些包含边 i→j 的简单循环。由此可见,其中一个简单循环包含边 i→j。如果存在一个包含i→j的简单圈C。然后我们可以将 C 上的每头奶牛分配给循环中下一头奶牛的礼物,C 上的每头奶牛最终都会过得更好。让所有不在 C 沿线的奶牛都收到它们原来的礼物。这对应于有效分布。

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

int N;
bitset<501> reachable[501];
vector<int> gifts[501];

void dfs(int src, int cur) {
	if (reachable[src][cur])
		return;
	reachable[src][cur] = true;
	for (int g : gifts[cur])
		dfs(src, g);
}

void calc_reachable_dfs() {
	for (int i = 1; i <= N; ++i)
		dfs(i, i);
}

void calc_reachable_floyd() {
	for (int i = 1; i <= N; ++i)
		for (int g : gifts[i])
			reachable[i][g] = true;
	for (int k = 1; k <= N; ++k) // run floyd-warshall
		for (int i = 1; i <= N; ++i)
			if (reachable[i][k])
				reachable[i] |= reachable[k];
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cin >> N;
	assert(N <= 500);
	for (int i = 1; i <= N; ++i) {
		gifts[i].resize(N);
		for (int &g : gifts[i])
			cin >> g;
		while (gifts[i].back() != i)
			gifts[i].pop_back();
	}
 
	calc_reachable_dfs(); 

	for (int i = 1; i <= N; ++i)
		for (int g : gifts[i])
			if (reachable[g][i]) {
				cout << g << "\n";
				break;
			}
}

Problem 2. Robot Instructions

Bessie is learning how to control a robot she has recently received as a gift.

The robot begins at point (0,0)(0,0) on the coordinate plane and Bessie wants the robot to end at point (xg,yg)(xg,yg). Bessie initially has a list of NN (1≤N≤401≤N≤40) instructions to give to the robot, the ii-th of which will move the robot xixi units right and yiyi units up (or left or down when xixi and yiyi are negative, respectively).

For each KK from 11 to NN, help Bessie count the number of ways she can select KK instructions from the original NN such that after the KK instructions are executed, the robot will end at point (xg,yg)(xg,yg).

**Note: the time and memory limits for this problem are 4s and 512MB, twice the defaults.**

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

The first line contains NN. The next line contains xgxg and ygyg, each in the range −109…109−109…109. The final NN lines describe the instructions. Each line has two integers xixi and yiyi, also in the range −109…109−109…109.

It is guaranteed that (xg,yg)≠(0,0)(xg,yg)≠(0,0) and (xi,yi)≠(0,0)(xi,yi)≠(0,0) for all ii.

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

Print NN lines, the number of ways Bessie can select KK instructions from the original NN for each KK from 11 to NN.

SAMPLE INPUT:

7
5 10
-2 0
3 0
4 0
5 0
0 10
0 -10
0 10

SAMPLE OUTPUT:

0
2
0
3
0
1
0

In this example, there are six ways Bessie can select the instructions:

(-2,0) (3,0) (4,0) (0,10) (0,-10) (0,10) (1 2 3 5 6 7)
(-2,0) (3,0) (4,0) (0,10) (1 2 3 5)
(-2,0) (3,0) (4,0) (0,10) (1 2 3 7)
(5,0) (0,10) (0,-10) (0,10) (4 5 6 7)
(5,0) (0,10) (4 5)
(5,0) (0,10) (4 7)

For the first way, the robot's path looks as follows:

(0,0) -> (-2,0) -> (1,0) -> (5,0) -> (5,10) -> (5,0) -> (5,10)

SCORING:

  • Test cases 2-4 satisfy N≤20N≤20.
  • Test cases 5-16 satisfy no additional constraints.

        我们在这个问题中所做的第一个观察是,操作发生的顺序并不重要,重要的是我们执行哪些操作。这意味着需要考虑 2N 组不同的操作。为了解决子任务,我们可以手动考虑每个集合。下面是 代码,它解决了 N≤20 的子任务。我们注意到问题的原始边界 N≤40,它只有两倍大。这建议将给定的操作分成大小大致相等的两半,并在两半上运行上述算法。如果我们遍历给定的列表之一,我们就会确切地知道我们需要从指令的另一半移动多远。

#include <bits/stdc++.h>

using namespace std;

using P = pair<long long, long long>;
P operator+(P a, P b) { return {a.first + b.first, a.second + b.second}; }
P operator-(P a, P b) { return {a.first - b.first, a.second - b.second}; }

vector<pair<P, int>> all_subsets(const vector<P> &dirs) {
	vector<pair<P, int>> v{
   
   {}};
	for (const P &d : dirs) {
		v.resize(2 * v.size());
		for (int i = 0; i < v.size() / 2; i++) {
			v[i + v.size() / 2] = {v[i].first + d, v[i].second + 1};
		}
	}
	sort(v.begin(), v.end());
	return v;
}

int main() {
	int N;
	cin >> N;
	P goal;
	cin >> goal.first >> goal.second;
	vector<P> dirs(N);
	for (auto &d : dirs) {
		cin >> d.first >> d.second;
	}
	vector<pair<P, int>> a =
		all_subsets(vector<P>(begin(dirs), begin(dirs) + N / 2));
	vector<pair<P, int>> b =
		all_subsets(vector<P>(begin(dirs) + N / 2, end(dirs)));
	reverse(b.begin(), b.end());
	vector<long long> ans(N + 1);
	vector<int> with_num;
	P rest_prev{1e18, 1e18};
	int ib = 0;
	for (const auto &[offset, num] : a) {
		const P rest = goal - offset;
		if (rest != rest_prev) {
			rest_prev = rest;
			with_num = vector<int>(N - N / 2 + 1);
			for (; ib < b.size() && b.at(ib).first > rest; ++ib);
			for (; ib < b.size() && b.at(ib).first == rest; ++ib) {
				++with_num.at(b.at(ib).second);
			}
		}
		for (int i = 0; i < with_num.size(); i++) {
			ans[i + num] += with_num[i];
		}
	}
	for (int i = 1; i <= N; i++) {
		cout << ans[i] << "\n";
	}
}

Problem 3. Email Filing

Farmer John has fallen behind on organizing his inbox. The way his screen is organized, there is a vertical list of folders on the left side of the screen and a vertical list of emails on the right side of the screen. There are MM total folders, numbered 1…M1…M (1≤M≤104)1≤M≤104). His inbox currently contains NN emails numbered 1…N1…N (1≤N≤1051≤N≤105); the iith email needs to be filed into folder fifi (1≤fi≤M1≤fi≤M).

FJ's screen is rather small, so he can only view KK (1≤K≤min(N,M)1≤K≤min(N,M)) folders and KK emails at once. Initially, his screen starts out displaying folders 1…K1…K on the left and emails 1…K1…K on the right. To access other folders and emails, he needs to scroll through these respective lists. For example, if he scrolls down one position in the list of folders, his screen will display folders 2…K+12…K+1, and then scrolling down one position further it will display folders 3…K+23…K+2. When FJ drags an email into a folder, the email disappears from the email list, and the emails after the one that disappeared shift up by one position. For example, if emails 1,2,3,4,51,2,3,4,5 are currently displayed and FJ drags email 3 into its appropriate folder, the email list will now show emails 1,2,4,5,61,2,4,5,6. FJ can only drag an email into the folder to which it needs to be filed.

Unfortunately, the scroll wheel on FJ's mouse is broken, and he can only scroll downwards, not upwards. The only way he can achieve some semblance of upward scrolling is if he is viewing the last set of KK emails in his email list, and he files one of these. In this case, the email list will again show the last KK emails that haven't yet been filed, effectively scrolling the top email up by one. If there are fewer than KK emails remaining, then all of them will be displayed.

Please help FJ determine if it is possible to file all of his emails.

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

The first line of input contains TT (1≤T≤101≤T≤10), the number of subcases in this input, all of which must be solved correctly to solve the input case. The TT subcases then follow. For each subcase, the first line of input contains MM, NN, and KK. The next line contains f1…fNf1…fN.

It is guaranteed that the sum of MM over all subcases does not exceed 104104, and that the sum of NN over all subcases does not exceed 105105.

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

Output TT lines, each one either containing either YES or NO, specifying whether FJ can successfully file all his emails in each of the TT subcases.

SAMPLE INPUT:

6
5 5 1
1 2 3 4 5
5 5 1
1 2 3 5 4
5 5 1
1 2 4 5 3
5 5 2
1 2 4 5 3
3 10 2
1 3 2 1 3 2 1 3 2 1
3 10 1
1 3 2 1 3 2 1 3 2 1

SAMPLE OUTPUT:

YES
YES
NO
YES
YES
NO

SCORING:

  • In inputs 2-10, the sum of MM over all subcases does not exceed 103103.
  • In inputs 11-12, no additional constraints.

        我们将按顺序遍历文件夹,跟踪最顶层的文件夹。我们还将按顺序遍历电子邮件,直到我们到达需要为给定最顶层文件夹归档的最后一封电子邮件。如果在屏幕上显示该电子邮件会导致窗口溢出,我们必须将最上面的电子邮件标记为已跳过。之后,如果我们可以归档电子邮件,我们应该立即这样做。否则,它位于窗口中。如果我们已经遍历了所有电子邮件,我们还必须模拟向上滚动我们之前跳过的电子邮件的行为。

#include <bits/stdc++.h>
 
using namespace std;
 
void rsolve() {
  int nfolder, nemail, windowsz;
  cin >> nfolder >> nemail >> windowsz;
  vector<int> emailtofolder(nemail);
  vector<vector<int>> foldertoemail(nfolder);
  vector<vector<int>> filetiming(nfolder);
  vector<bool> filed(nemail);
  vector<bool> skipped(nemail);
  vector<bool> inwindow(nemail);
  for(int i = 0; i < nemail; i++) {
    cin >> emailtofolder[i];
    filetiming[max(0, --emailtofolder[i] - windowsz + 1)].push_back(i);
    foldertoemail[emailtofolder[i]].push_back(i);
  }
  int currentemail = 0;
  int lhsemail = 0;
  int numinwindow = 0;
  int rhsemail = nemail-1;
  auto fileemail = [&](int id) -> void {
    if(inwindow[id]) {
      inwindow[id] = false;
      numinwindow--;
    }
    assert(!filed[id]);
    filed[id] = true;
  };
  int bottom = 0;
  for(int i = 0; i < nfolder; i++) {
    // file anything that can be newly filed
    if(i > bottom && i + windowsz <= nfolder) bottom++;
    for(int out: filetiming[i]) if(inwindow[out]) fileemail(out);
    while(foldertoemail[i].size() && currentemail <= foldertoemail[i].back()) {
      // the window is full so in order to consider this email, we must scroll past the current one
      if(numinwindow == windowsz) {
        while(!inwindow[lhsemail]) lhsemail++;
        skipped[lhsemail] = true;
        inwindow[lhsemail] = false;
        numinwindow--;
      }
      if(emailtofolder[currentemail] >= i && emailtofolder[currentemail] <= i + windowsz - 1) {
        // can file
        filed[currentemail++] = true;
        continue;
      }
      inwindow[currentemail++] = true; numinwindow++;
    }
    // scroll through emails that would be implicitly loaded
    while(currentemail < nemail && numinwindow < windowsz) {
      if(emailtofolder[currentemail] >= i && emailtofolder[currentemail] <= i + windowsz - 1) {
        // can file
        filed[currentemail++] = true;
        continue;
      }
      inwindow[currentemail++] = true; numinwindow++;
    }
    // scroll up emails since we've hit the end
    if(currentemail == nemail) {
      while(numinwindow < windowsz) {
        if(rhsemail < 0) break;
        if(!skipped[rhsemail]) {
          rhsemail--;
          continue;
        }
        if(emailtofolder[rhsemail] < bottom) {
          cout << "NO\n";
          return;
        }
        if(emailtofolder[rhsemail] <= bottom + windowsz - 1) {
          filed[rhsemail--] = true;
          continue;
        }
        inwindow[rhsemail--] = true; numinwindow++;
      }
    }
  }
  for(auto out: filed) {
    if(!out) {
      cout << "NO\n";
      return;
    }
  }
  cout << "YES\n";
}
 
void solve() {
  int t;
  cin >> t;
  while(t--) rsolve();
}

int main() {
  ios_base::sync_with_stdio(false);
  cin.tie(NULL);
  solve();
}

猜你喜欢

转载自blog.csdn.net/GeekAlice/article/details/128172798