洛谷 11 月月賽 I & MCOI Round 3 Div.2 -- 村國
題目(https://www.luogu.com.cn/problem/P7043?contestId=36089):
C 國一共有 N 個村莊,N−1條道路。這些道路都可以雙向通行。保證小 S 可以從一座村莊到其他任何一座村莊。這 N 個村莊編號為 1 到 N。
剛開始小 S 對第 i 個村莊的好感值為 Ai。小 S 的假期一共有 M 天,他會在 C 國旅行一共M天。每一天他會選擇來到當前好感值最高的村莊。如果有好感值相同的村莊,他會選擇編號最小的村莊。假設這一天他來到村莊 X,那麼這一天結束後,與村莊X直接相鄰所有村莊的好感值都會增加 1。即能從 X 出發僅經過一條道路到達的村莊好感值會增加 1。因為小 S 已經在村莊 X 待過一天了,所以這一天結束後村莊 X 的好感值並不會增加。
現在小 S 想要知道經過 MM 天的旅行後好感值最高的村莊。
如果有多個好感值最高的村莊,輸出編號最小的。
輸入格式
本題單個測試點包含多組測試資料。
第一行一個正整數 T 表示資料組數。
對於每組資料:
第一行包括兩個正整數 N,M,表示村莊個數和旅行天數。
接下來一行 N 個整數,第 i 個整數表示第 i 座村莊的好感值 Ai。
接下來 N−1 行。每行兩個整數 x,y 表示村莊x和村莊y之間有一條雙向通行的道路。
輸出格式
一個整數表示 M 天結束後好感值最高的村莊的編號。如果有多個好感值最高的村莊,輸出編號最小的。
輸入輸出樣例
輸入
2
2 3
2 6
1 2
3 5
2 6 4
1 3
2 3
輸出
2
3
樣例說明
對於第一組資料,小 S 在 2 號村莊旅行了 3 天,結束時村莊 1,2 的好感值分別為 5,6。所以答案輸出 2。
對於第二組資料,結束時三個村莊的好感值分別為 3,7,8,所以答案輸出 3。
資料規模與約定
對於 100% 的資料,1≤N≤2×10^6,1≤M≤10^18,1<=Ai<=2^31 - 1,1 <= T <= 10。
題解:
先列坑點:
- N = 1的情況(特判)
- “在村莊 X 待過一天了,所以這一天結束後村莊 X 的好感值並不會增加。”這句話的意思是他去訪問那天不會增加,而不是說下一次訪問到他相鄰的時村莊時他再也不會增加(閱讀理解,直接打臉)
解題思路:
-
暴力騙分(30分),按照題意直接每次找出最大好感度村莊,然後將它周圍村莊依次好感度加一,最後輸出最大好感度村莊就完事。
-
模擬思維(100分):實際上最終結果肯定是一開始最大的好感度的村莊A,或者是A相鄰的最大好感度村莊B,肯定是這二者之一,核心就在這。接著開始模擬:
- 如果只有一個村莊,直接輸出它的編號(特判)
- 如果A與B相差的好感度小於旅行的天數,那麼必然是A
- 如果A與B相差的好感度等於旅行的天數,或者A與B的好感度一開始就是相同的,那麼肯定是取A與B編號最小的(A與B字典序最小)那個村莊
- 如果A與B相差的好感度大於旅行的天數,此時就要看剩餘的天數是偶數還是奇數,若是奇數,則取A與B編號最大的(A與B字典序最大)那個村莊;若是偶數,則取A與B編號最小的(A與B字典序最小)那個村莊。
30分程式碼:
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<vector>
#include<utility>
#include<string.h>
using namespace std;
typedef long long LL;
const int N = 2e6 + 5;
pair<LL, LL> a[N];
vector<LL> map[N];
int T;
LL n, m , ans = -1, ansCode = -1;
int main()
{
ios::sync_with_stdio(false);
cin >> T;
while(T --)
{
memset(map, 0, sizeof(map));
memset(a, 0, sizeof(a));
ans = -1, ansCode = -1;
cin >> n >> m;
for(int i = 0; i < n; i++)
{
cin >> a[i].second; //村莊好感度
a[i].first = i; //村莊編號
}
for(int i = 0; i < n - 1; i++)
{
int u, v;
cin >> u >> v;
u--, v--;
map[u].push_back(v);
map[v].push_back(u);
}
while(m --)
{
LL nowCode = -1, tempVal = -1;
for(int i = 0; i < n; i++)
{
if(a[i].second > tempVal)
{
tempVal = a[i].second;
nowCode = i;
}
}
for(int i = 0; i < map[nowCode].size(); i++)
{
LL code = map[nowCode][i];
a[code].second ++;
}
}
for(int i = 0; i < n; i++)
{
if(a[i].second > ans)
{
ans = a[i].second;
ansCode = i;
}
}
ansCode ++;
cout << ansCode << endl;
}
return 0;
}
100分程式碼:
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<vector>
#include<utility>
#include<string.h>
using namespace std;
typedef long long LL;
const int N = 2e6 + 5;
pair<LL, LL> a[N];
vector<LL> map[N];
int T;
LL n, m;
int main()
{
ios::sync_with_stdio(false);
cin >> T;
while(T --)
{
memset(map, 0, sizeof(map));
memset(a, 0, sizeof(a));
cin >> n >> m;
for(int i = 0; i < n; i++)
{
cin >> a[i].second; //村莊好感度
a[i].first = i; //村莊編號
}
for(int i = 0; i < n - 1; i++)
{
int u, v;
cin >> u >> v;
u--, v--;
map[u].push_back(v);
map[v].push_back(u);
}
/*
最大好感度村莊序號A(字典序最小),A相鄰最大好感度村莊B(字典序最小)。
結果必其一
*/
LL pos1 = -1, pos2 = -1, tempVal1 = -1, tempVal2 = -1;
for(int i = 0; i < n; i++) //找村莊A
{
if(a[i].second > tempVal1)
{
tempVal1 = a[i].second;
pos1 = i;
}
}
for(int i = 0; i < map[pos1].size(); i++) //找村莊B
{
LL nowPos = map[pos1][i];
if(a[nowPos].second > tempVal2)
{
tempVal2 = a[nowPos].second;
pos2 = nowPos;
}
}
LL disVal = a[pos1].second - a[pos2].second; //A與B相差的好感度
if(n == 1) cout << pos1 + 1 << endl;
else
{
if(disVal > m) cout << pos1 + 1 << endl;
else if(disVal == m || disVal == 0) cout << min(pos1 + 1, pos2 + 1) << endl;
else
{
m -= disVal;
if(m % 2 != 0) cout << max(pos1 + 1, pos2 + 1) << endl;
else cout << min(pos1 + 1, pos2 + 1) << endl;
}
}
}
return 0;
}