洛谷 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×106,1≤M≤1018,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;
}