USACO 2022 賽季試題解析係列(1月晉級賽-銅級&銀級)

USACO 2021-2022 賽季最後一次月賽在今天開考,部分同學已經完成了考試,也意味著本賽季落下帷幕。

為(wei) 方便同學了解和自查題目思路,USACO 教學組字繼續本賽的USACO題解》專(zhuan) 題繼續提供專(zhuan) 業(ye) 解題,本期進入第三期,提供2022賽季第2次月賽中的銅升銀、銀升金解題思路,希望能為(wei) 同學們(men) 提供有用的幫助,埋下解題思維,提前備考USACO2022-2023賽季。

12月銅級題解回顧請點擊:【幹貨】

12月銀級題解回顧請點擊:【幹貨】

【幹貨】USACO 2022 賽季試題解析係列(1月晉級賽-銅級&銀級)

Bronze Problem1

Herdle

【幹貨】USACO 2022 賽季試題解析係列(1月晉級賽-銅級&銀級)

【幹貨】USACO 2022 賽季試題解析係列(1月晉級賽-銅級&銀級)

【幹貨】USACO 2022 賽季試題解析係列(1月晉級賽-銅級&銀級)

這是一道簡單的搜索題目,奶牛們(men) 發明了一個(ge) 3X3的填字小遊戲,可以添加A-Z的任何一個(ge) 大寫(xie) 字母(也就是26種情況),我們(men) 來看一下官方所給我們(men) 的示例輸入1:

【幹貨】USACO 2022 賽季試題解析係列(1月晉級賽-銅級&銀級)

示例輸入2:

【幹貨】USACO 2022 賽季試題解析係列(1月晉級賽-銅級&銀級)

我們(men) 把它簡易的看成一個(ge) 3X3的坐標,第1行第1列我們(men) 就稱之為(wei) (1,1),以此類推我們(men) 發現在這個(ge) 例子當中輸入1和輸入2的(3,2)是完全匹配的,也就是我們(men) 說的綠色方塊,本圖中隻有一個(ge) 綠色方塊,再來看一下W這個(ge) 字母分別都出現在了示例1和示例二當中,但是位置不相同,分別是(1,3)和(1,1),這樣的方塊我們(men) 標記成為(wei) 黃色方塊,本題的任務目標就是分別輸出綠色方塊和黃色方塊的數量。

上麵我們(men) 也說過了可以把它們(men) 分別看成3X3的矩陣,那麽(me) 最好的處理方式就是二維數組了:

我們(men) 先來看看綠色方塊的獲得方式,當位置和內(nei) 容都完全匹配的時候,可以增加綠色方塊的數量。

下麵我們(men) 先來做一些準備工作:

#include<bits/stdc++.h>

using namespace std;

int main()

{

int ans1 = 0, ans2 = 0;

//用來儲(chu) 存綠色方塊和黃色方塊的數量。

string corr[3];

string gues[3];

//定義(yi) 兩(liang) 個(ge) 字符串數組,分別儲(chu) 存輸入1和輸入2的內(nei) 容。

for (int i = 0; i < 3; i++)

cin >> corr[i];

for (int i = 0; i < 3; i++)

cin >> gues[i];

//輸入樣例1和樣例2。

return 0;

}

前麵我們(men) 說到了填字為(wei) A-Z一共26個(ge) 字符,我們(men) 可以記錄下每個(ge) 樣例中相應字母出現的次數,比如黃色W都出現了一次。(關(guan) 鍵代碼)

int cor[27] = {0};

int gue[27] = {0};

//記錄元素出現次數。

for (int i = 0; i < 3; i++)

for (int j = 0; j < 3; j++)

{

if (corr[i][j] == gues[i][j]) ans1++;

//記錄綠色方塊數量。

else

{

cor[corr[i][j] - 'A']++;

gue[gues[i][j] - 'A']++;

}

}

這裏是關(guan) 鍵代碼,請同學們(men) 認真閱讀,通過雙重循環遍曆3X3的數組,得到實際的字符,比如說B,那我們(men) 就要記錄下B出現的次數,也就是說出現一次就加一,這也就是cor[27]和gue[27]的作用,記錄下出現相應單詞出現的次數。我們(men) 隻需要記錄下26個(ge) 英文單詞,比如’A’-’A’就是0,我們(men) 就可以儲(chu) 存在相應的數組位置中,當位置與(yu) 內(nei) 容完全匹配就可以增加綠色的方塊,否則就儲(chu) 存在兩(liang) 個(ge) 數組中。

for (int i = 0; i < 26; i++)

ans2 += min(cor[i], gue[i]);

黃色方塊就是相同字符出現,兩(liang) 個(ge) 數組的最小值。如下是完整代碼:

代碼如下:

#include<bits/stdc++.h>

using namespace std;

int main()

{

string corr[3];

string gues[3];

int cor[27] = {0};

int gue[27] = {0};

int ans1 = 0, ans2 = 0;

for (int i = 0; i < 3; i++)

cin >> corr[i];

for (int i = 0; i < 3; i++)

cin >> gues[i];

for (int i = 0; i < 3; i++)

for (int j = 0; j < 3; j++)

{

if (corr[i][j] == gues[i][j]) ans1++;

else

{

cor[corr[i][j] - 'A']++;

gue[gues[i][j] - 'A']++;

}

}

for (int i = 0; i < 26; i++)

ans2 += min(cor[i], gue[i]);

cout << ans1 << endl << ans2;

return 0;

}

(向下滑動,查看所有代碼)

Bronze Problem2

NON-TRANSITIVE DICE

 

【幹貨】USACO 2022 賽季試題解析係列(1月晉級賽-銅級&銀級)

題意為(wei) 一個(ge) n個(ge) 點m條邊的無向圖,點的編號為(wei) 1到n,可以在任意點i與(yu) 點j之間連邊,代價(jia) 為(wei) (i-j)2,現在可以最多連2條邊,使得1與(yu) n連通,求最小代價(jia) 。

題解:奶牛們(men) 特別喜歡玩一個(ge) 骰子遊戲,這個(ge) 骰子是一個(ge) 四麵骰,一共隻有4麵哦!它們(men) 所進行的就是投骰子大比拚,使用兩(liang) 個(ge) 骰子X和Y進行比賽,當投出相同的數字就再來一次,誰的點數高誰就獲得了勝利,如果在概率上X比Y更有可能取勝,那麽(me) 我們(men) 就可以說X打敗了Y。我們(men) 來看一下官方給我們(men) 的樣例數據:

骰子A:4,5,6,7

骰子B:2,4,5,10

骰子C:1,4,8,9

好的我們(men) 先來看一下怎麽(me) 判定哪個(ge) 骰子能打敗哪個(ge) 骰子呢?就從(cong) A和B入手一下分析看看。骰子A一共可能有4種情況,骰子B也是有四種情況,那麽(me) 兩(liang) 個(ge) 骰子進行大比拚的話一共有且僅(jin) 有4X4=16種情況,我們(men) 來把每種情況逐一來進行分析,統計A或者B勝利的次數,A多就是A贏麵大,B多就是B贏麵大,相同就是贏麵一樣。

這種代碼應該怎麽(me) 寫(xie) 呢?其實兩(liang) 個(ge) for循環就可以滿足你。

代碼如下:

for(int i = 0; i < 4; i++)

{

for(int j = 0; j < 4; j++)

{

if(dice1[i] > dice2[j])

{

win += 1;

}

if(dice1[i] < dice2[j])

{

lose += 1;

}

}

}

這裏的win代表的就是A的勝利次數,lose就是B的勝利次數,最終結果win大就是A贏,lose大就是B贏,相同就平局,在這裏我們(men) 不如把它封裝成一個(ge) 函數。

int check(int dice1[], int dice2[])

{

int win = 0;

int lose = 0;

for(int i = 0; i < 4; i++)

{

for(int j = 0; j < 4; j++)

{

if(dice1[i] > dice2[j])

{

win += 1;

}

if(dice1[i] < dice2[j])

{

lose += 1;

}

}

}

if (win > lose)

{

return 1;

}

else if (win < lose)

{

return -1;

}

else

{

return 0;

}

}

Check函數的功能就是判斷兩(liang) 個(ge) 數組(骰子)誰的贏麵更大。

好的,我們(men) 下麵再來討論一個(ge) 重要的問題,現在引入了另外一個(ge) 骰子C,剛剛有官方的輸入樣例,C比較特殊,滿足A比B的贏麵大,B比C的贏麵大,而C的贏麵要比A大,就像一個(ge) 循環一樣。我們(men) 要解決(jue) 的問題就是給你兩(liang) 個(ge) 骰子A和B,讓你判斷是否存在C,正好可以構成A beat B,B beat C,C beat A。當然也可以這樣,B beat A,A beat C,C beat B,隻要是滿足於(yu) 我們(men) 這種關(guan) 係的均可。

那麽(me) 如何找到C呢?C又是否存在呢?

其實這一題有一個(ge) 非常暴力但是很好理解的方法:骰子的數據取值範圍是1到10,那麽(me) 骰子C的可能性無外乎是10X10X10X10=10000種情況。我們(men) 把每一種C的情況都列出來,一種種的進行匹配,隻要是滿足於(yu) 我們(men) 的兩(liang) 兩(liang) 比較,就可以結束了,當全部情況都不滿足時,我們(men) 也就可以說C不存在,我們(men) 來看一段很長的代碼但是邏輯非常的簡單:

int solve(int dice1[], int dice2[])

{

int dice3[4] = {0};

for(int d1 = 1; d1 <= 10; d1++)

{

dice3[0] = d1;

for(int d2 = 1; d2 <= 10; d2++)

{

dice3[1] = d2;

for(int d3 = 1; d3 <= 10; d3++)

{

dice3[2] = d3;

for(int d4 = 1; d4 <= 10; d4++)

{

dice3[3] = d4;

if(check(dice3, dice1) == 1 and check(dice3, dice2) == -1 and check(dice2,dice1) == -1)

{

return 1;

}

if(check(dice3, dice1) == -1 and check(dice3, dice2) == 1 and check(dice2, dice1) == 1)

{

return 1;

}

}

}

}

}

return 0;

}

函數solve的功能就是列出10000種C的可能性,一種種的進行匹配,隻要是滿足我們(men) 上方所說的規律就可以返回1結束程序,全部運行結束都不滿足情況就可以返回0了。由此最棘手的兩(liang) 個(ge) 問題都被我們(men) 所解決(jue) 了,下麵我們(men) 來看看完整代碼:

#include <iosestream>

using namespace std;

int dice1[4], dice2[4];

int check(int dice1[], int dice2[])

{

int win = 0;

int lose = 0;

for(int i = 0; i < 4; i++)

{

for(int j = 0; j < 4; j++)

{

if(dice1[i] > dice2[j])

{

win += 1;

}

if(dice1[i] < dice2[j])

{

lose += 1;

}

}

}

if (win > lose)

{

return 1;

}

else if (win < lose)

{

return -1;

}

else

{

return 0;

}

}

int solve(int dice1[], int dice2[])

{

int dice3[4] = {0};

for(int d1 = 1; d1 <= 10; d1++)

{

dice3[0] = d1;

for(int d2 = 1; d2 <= 10; d2++)

{

dice3[1] = d2;

for(int d3 = 1; d3 <= 10; d3++)

{

dice3[2] = d3;

for(int d4 = 1; d4 <= 10; d4++)

{

dice3[3] = d4;

if(check(dice3, dice1) == 1 and check(dice3, dice2) == -1 and check(dice2,dice1) == -1)

{

return 1;

}

if(check(dice3, dice1) == -1 and check(dice3, dice2) == 1 and check(dice2, dice1) == 1)

{

return 1;

}

}

}

}

}

return 0;

}

int main()

{

int cases;

cin >> cases;

//cases代表了我們(men) 輸入了幾種情況,比如3,就是有3組骰子等待我們(men) 去判定是否存在C。

while(cases--)

{

for(int i = 0; i < 4; i++)

{

cin >> dice1[i];

//輸入A骰子的數據

}

for(int i = 0; i < 4; i++)

{

cin >> dice2[i];

//輸入B骰子的數據

}

if(solve(dice1, dice2))

{

cout << "yes" << endl;

}

else

{

cout << "no" << endl;

}

}

return 0;

}

(向下滑動,查看所有代碼)

Bronze Problem3

DROUGHT

 

【幹貨】USACO 2022 賽季試題解析係列(1月晉級賽-銅級&銀級)

【幹貨】USACO 2022 賽季試題解析係列(1月晉級賽-銅級&銀級)

題解:我們(men) 要去買(mai) 玉米去喂養(yang) 奶牛,但是這些奶牛非常的矯情。奶牛進食需要另外一頭奶牛陪著吃,必須給相鄰的奶牛同時喂玉米才能降低它們(men) 的饑餓水平,我們(men) 想要達到的效果就是通過給兩(liang) 頭相鄰的牛喂玉米,讓每一頭奶牛的饑餓值剛好都相同,如果可行的話我們(men) 就輸出使用的玉米袋數,否則就輸出-1。我們(men) 先來分析一下官方給的樣例數據吧。

輸入樣例:

5 3

8 10 5

6

4 6 4 4 6 4

3

0 1 0

2

1 2

3

10 9 9

我們(men) 來看一看應該怎樣處理這些數據,首先數字5代表了我們(men) 有5組數據等待我們(men) 判定出結果,接下來的3代表了本次有3頭牛要喂,其中饑餓度為(wei) 8 10 5。我們(men) 怎麽(me) 處理這些數據從(cong) 而看出這些牛是否能同時達到相同的饑餓度呢?如果可以達到,我們(men) 怎麽(me) 得出需要喂多少袋玉米呢?

這時候我們(men) 可以看成兩(liang) 組數據,8和10是綁定的,同時減少,10和5是綁定的,同時減少,每次必須同時喂兩(liang) 頭牛,牛一旦到達饑餓度為(wei) 0的時候就不再吃東(dong) 西了。對於(yu) 8 10 5來說,官方解答給2號和3號兩(liang) 袋,變成7和3,再給1號和2號五袋變成3和3,這樣三頭牛的饑餓水平都變成了3,消耗了2X2+2X5=14袋玉米,那麽(me) 我們(men) 就應該輸出14。

其實在這一題當中我們(men) 需要運用一些數學知識,也就是滿足單調性原則,很明顯,令我們(men) 當前使每個(ge) 奶牛的饑餓值降至 x(保證降至 x 是可行的,最起碼不小於(yu) 0)。

  1. a1>=x

  2. a2-x>=a1-x 所得a2>=a1

  3. a3-x>=(a2-x)-(a1-x) 所得a3-x >= a2-a1 x<=a3-a2+a1

  4. 可以推斷出x<=an+a(n-1)+…-a2+a1

接下來我們(men) 嚐試做一下準備工作:

int t;

//t為(wei) 多少組數據

cin >> t;

while (t--)

{

int n;

cin >> n;

//這組數據中一共有幾個(ge) 數字。

bool flag = true;

//判定是否滿足條件,false代表不可達成統一的饑餓度。

long long x, sum = 0;

//x為(wei) 最終達到的饑餓度,sum是差值。

long long a[n+1] = {0};

//設定數組用來存放處理數字

for (int i = 1; i <= n; i++)

cin >> a[i];

x = a[1];

}

接下來我們(men) 可以通過一次循環來解決(jue) 這個(ge) 問題,從(cong) 而實現O(n)的時間複雜度,先來解決(jue) 輸出-1的情況(flag==false)。

for (int i = 1; i <= n; i++)

{

sum = a[i] - sum;

//記錄下差值

if(i % 2)

//偶數項內(nei) 容

{

x = min(x, sum);

if (x < 0)

{

flag = false;

//此時x小於(yu) 0說明饑餓度小於(yu) 0了(奶牛吃飽了會(hui) 不吃的)

break;

}

}

else

{

if (i == n && sum)

{

flag = false;

//到達尾部

break;

}

if (sum < 0)

{

flag = false;

//差值不可小於(yu) 0

break;

}

}

}

最後補上輸出-1。

if (!flag)

cout << -1 << endl;

除此之外我們(men) 累加an-x的差值即可獲得玉米的消耗袋數。

else

{

long long ans = 0;

for (int i = 1; i <= n; i++)

ans += a[i] - x;

//把差值累加起來得到玉米數量。

cout << ans << endl;

}

此時我們(men) 的任務就已經完成了,下麵來看一下完整代碼。

代碼如下:

#include<bits/stdc++.h>

using namespace std;

int main()

{

int t;

cin >> t;

while (t--)

{

int n;

cin >> n;

bool flag = true;

long long x, sum = 0;

long long a[n+1] = {0};

for (int i = 1; i <= n; i++)

cin >> a[i];

x = a[1];

for (int i = 1; i <= n; i++)

{

sum = a[i] - sum;

if(i % 2)

{

x = min(x, sum);

if (x < 0)

{

flag = false;

break;

}

}

else

{

if (i == n && sum)

{

flag = false;

break;

}

if (sum < 0)

{

flag = false;

break;

}

}

}

if (!flag)

cout << -1 << endl;

else

{

long long ans = 0;

for (int i = 1; i <= n; i++)

ans += a[i] - x;

cout << ans << endl;

}

}

return 0;

}

(向下滑動,查看所有代碼)

【幹貨】USACO 2022 賽季試題解析係列(1月晉級賽-銅級&銀級)

Silver Problem1

Searching for Soulmates

 

【幹貨】USACO 2022 賽季試題解析係列(1月晉級賽-銅級&銀級)

【幹貨】USACO 2022 賽季試題解析係列(1月晉級賽-銅級&銀級)

首先,題目很好懂,A通過三種轉換到達B,第一種是乘以2,第二種是除以2,第三種是加1,很容易聯想到BFS,但是時間複雜度較高,隻能滿足前4個(ge) 點。

題目明確說了,奇數不能直接除以2,那麽(me) 我們(men) 就可以聯想到二進製,乘以二是左移1位,除以二是右移一位,奇數加一。

先考慮其中一個(ge) 情況:a>b。會(hui) 怎麽(me) 做?

這就非常容易考慮。讓 a 不斷加 1 與(yu) 除以 2 直到 b>a,這樣 a 便可以一路加到 b 去。

代入樣例會(hui) 發現這樣一個(ge) 情況:a 會(hui) 一開始不斷除以 2 和加 1,一旦開始乘 2 後就不會(hui) 再除以 2。

原因其實很好理解,若除以 2 後乘 2 對答案不會(hui) 更優(you) 。

也就是說一旦開始左移那麽(me) 其中加入的右移會(hui) 抵消掉之前的左移,沒有意義(yi) 。

那麽(me) 就和我們(men) 考慮的情況類似了,也就是說,我們(men) 需要找一個(ge) 數 t,作為(wei) 我們(men) 的基數。負責讓我們(men) 完成a>b的情況。那麽(me) 這個(ge) 中轉值後麵隻允許乘 2 和加 1。所以,t 的二進製一定是 b 二進製表示下的一個(ge) 前綴。

所以,我們(men) 隻需要枚舉(ju) b 二進製前綴表示的數字,即 t。然後按照分析的情況求出 a 變為(wei) t 所花的次數。這都求出來了,還想不到後麵怎麽(me) 寫(xie) ?不斷將 a 乘 2 和加 1,使其與(yu) b 的前綴保證相同即可。

代碼如下:

#include <bits/stdc++.h>

#define ll long long

using namespace std;

ll n;

ll len(ll num) {

for (ll i = 63; i >= 0; i--) {

if ((num >> i) & 1) {

return i + 1;

}

}

return 0;

}

ll get(ll num, ll i, ll len) {

return num >> (len - i);

}

void solve() {

ll a, b, c, cnt = 0, ans = 2e9;

cin >> a >> b;

if (a == b) {

cout << 0 << endl;

return;

}

c = len(b);

ll save = a;

for (ll i = 1; i <= c; i++, ans = min(ans, cnt), a = save) {

cnt = 0;

ll nowb = get(b, i, c);

while (a != nowb) {

if (a > nowb) {

if (a & 1) a++;

else a /= 2;

cnt++;

} else {

cnt += nowb - a;

a = nowb;

}

}

for (ll j = i + 1; j <= c; j++) {

nowb = get(b, j, c);

a <<= 1;

cnt++;

if (nowb != a) a++, cnt++;

}

}

cout << ans << endl;

}

int main() {

cin >> n;

while (n--) {

solve();

}

return 0;

}

Silver Problem2

Cow Frisbee

 

【幹貨】USACO 2022 賽季試題解析係列(1月晉級賽-銅級&銀級)

題意可以簡化為(wei) ,在一排牛中,對於(yu) 每兩(liang) 頭牛,如果兩(liang) 頭牛之間沒有牛比這兩(liang) 座高,則答案累加這兩(liang) 頭牛的距離。

直接枚舉(ju) 兩(liang) 頭牛的高度,再遍曆其中所有牛,符合條件就統計,這種方式時間複雜度為(wei) O(n^3)

再進一步分析我們(men) 發現,兩(liang) 頭牛之間沒有比他倆(lia) 高的,那麽(me) 遍曆完的牛還能和後麵的牛組成對的必然是單調遞減的,那麽(me) 維護一個(ge) 單調棧就能夠在O(n)時間內(nei) 解決(jue) 。

代碼如下:

#include <bits/stdc++.h>

using namespace std;

int main()

{

int n;

cin >> n;

int height[n + 1];

for (int i = 1; i <= n; i++)

{

cin >> height[i];

}

stack<long long> dec_st;

dec_st.push(1);

long long ans = 0;

for (long long i = 2; i <= n; i++)

{

while (!dec_st.empty() && height[dec_st.top()] < height[i])

{

ans += i - dec_st.top() + 1;

dec_st.pop();

}

if (!dec_st.empty())

{

ans += i - dec_st.top() + 1;

}

dec_st.push(i);

}

cout << ans << endl;

return 0;

}

Silver Problem3

Cereal 2

 

【幹貨】USACO 2022 賽季試題解析係列(1月晉級賽-銅級&銀級)

【幹貨】USACO 2022 賽季試題解析係列(1月晉級賽-銅級&銀級)

【幹貨】USACO 2022 賽季試題解析係列(1月晉級賽-銅級&銀級)

通過讀題我們(men) 可以知道本題有兩(liang) 個(ge) 問題:

1. 最少有多少頭牛會(hui) hungry

2. 輸出一個(ge) 保證最多牛不餓的序列

首先通過題目描述我們(men) 可以明確每頭牛有2種喜歡的飼料,碰到這種二元關(guan) 係的題,我們(men) 通常可以考慮從(cong) 圖論的角度去思考。把牛當作一個(ge) 集合,麥片當作另一個(ge) 集合,從(cong) 牛到飼料進行連邊,我們(men) 就得到了一個(ge) 二分圖。第一個(ge) 問題就是求這個(ge) 二分圖的最大匹配,通常我們(men) 用的是匈牙利算法,其正確性基於(yu) hall 定理,本質是不斷尋找增廣路來擴大匹配數。但是其正確性證明比較複雜,在此略去。

匈牙利算法的過程是,枚舉(ju) 每一個(ge) 左部點 u ,然後枚舉(ju) 該左部點連出的邊,對於(yu) 一個(ge) 出點 v,如果它沒有被先前的左部點匹配,那麽(me) 直接將 u 匹配 v,否則嚐試讓 v 的“原配”左部點去匹配其他右部點,如果“原配”匹配到了其他點,那麽(me) 將 u 匹配 v,否則 u 失配。

嚐試讓“原配”尋找其他匹配的過程可以遞歸進行。需要注意的是,在一輪遞歸中,每個(ge) 右部點隻能被訪問一次。

第一問解決(jue) 後我們(men) 會(hui) 得到一組匹配關(guan) 係,如何把匹配關(guan) 係變成序列呢?答案就是拓撲排序,我們(men) 需要遍曆每種麥片來構建一個(ge) 具有先後關(guan) 係的有向圖,那麽(me) 隻有以下情況:

1. 當前牛匹配的是自己第一選擇,那麽(me) 不做處理

2. 當前牛匹配的是自己第二選擇,那麽(me) 需要排在搶掉自己第一選擇的牛後麵,也就是加一條從(cong) 那頭牛指向自己的有向邊。

3. 當前牛沒有匹配,那麽(me) 需要對搶了自己的兩(liang) 個(ge) 選擇的牛分別加有向邊。

然後輸出拓撲序就可以了。

代碼如下:

#include<bits/stdc++.h>

using namespace std;

const int maxn=15;//邊數的最大值

int n, m, t;

int mch[maxn], vistime[maxn];

int ind[maxn];

vector<int> e[maxn];

vector<int> p[maxn];

vector<int> ts;

bool dfs(const int u, const int tag) {

if (vistime[u] == tag) return false;

vistime[u] = tag;

for (auto v : e[u])

if ((mch[v] == 0) || dfs(mch[v], tag))

{

mch[v] = u;

return true;

}

return false;

}

void solve()

{

for (int i = 1; i <= m; i++)

{

int x = mch[i];

if (!x) continue;

if (e[x][0] != i)

{

p[mch[e[x][0]]].push_back(x);

ind[x]++;

if (e[x][1] != i)

{

p[mch[e[x][1]]].push_back(x);

ind[x]++;

}

}

}

}

void topo()

{

queue<int> q;

for (int i = 1; i <= n; i++)

if (ind[i] == 0) {

q.push(i); ts.push_back(i);

}

while (!q.empty())

{

int x = q.front(); q.pop();

for (auto i : p[x])

{

ind[i]--;

if (!ind[i])

{

q.push(i);

ts.push_back(i);

}

}

}

}

int main() {

cin >> n >> m;

int u, v;

for (int i = 1; i <= n; i++) {

cin >> u >> v;

e[i].push_back(u);

e[i].push_back(v);

}

int ans = 0;

for (int i = 1; i <= n; ++i) if (dfs(i, i))

{

++ans;

}

cout << n - ans << endl;

solve();

topo();

for(auto i : ts)

cout << i << endl;

return 0;

}

本期 USACO 2021-2022賽季1月月賽銅升銀、銀升金題解就到這裏了,我們(men) 下周金升鉑金題解見,敬請期待!

 

USACO 題目的特色就在於(yu) 靈活。對於(yu) 這種線上比賽來說,如果題目能簡單的套用算法,隨便搜索一下對應的算法代碼,那這個(ge) 比賽還有什麽(me) 參與(yu) 的價(jia) 值?

USACO 考題的側(ce) 重點就在於(yu) 考核你使用算法分析問題的能力,所以平時做題的時候,可以把題目的問題類型和算法做一個(ge) 關(guan) 聯對應,這樣更容易從(cong) 問題入手,快速聯想到對應算法。

1月的月賽即將來臨(lin) ,這次沒能順利通過的小夥(huo) 伴們(men) 要在性能優(you) 化方麵刻意練習(xi) 一下,相信在競賽中會(hui) 有更加優(you) 秀的表現!

【競賽報名/項目谘詢+微信:mollywei007】

上一篇

大學生競賽匯總!適合大學生參加的加分/保研/專利競賽項目

下一篇

2022A-level考試局、考試科目及選科建議!

你也可能喜歡

  • 暫無相關文章!

評論已經被關(guan) 閉。

插入圖片
返回頂部