P7043「MCOI-03」村国

题面大意:一棵树,每次选择一个权值最大的点(特别的,若有多个权值相同的点,则选择编号最小的),并令周围的一圈点权值都加一,操作m次,最终权值最大的那个节点是哪个?其中m属于1e18

如此之大的数据量启示我们找规律或者矩阵乘法,显然此题没办法矩乘,所以只能找规律

考虑我们第一次找出来的最合适的点\(fa\),考虑每次操作与\(fa\)最合适的儿子\(son\)权值的关系

1)当我即使用尽所有的次数,未能使得son与fa的权值相等,显然fa是最合适的

2)当son权值跟自己一样,但是操作次数已经用完了,显然这时候最优的点一定是自己与儿子之间编号最小的那个节点

3)当son权值跟自己一样,但是依然还有剩余的一些次数,显然,我的答案只可能在\(fa\)\(son\)之间反复横跳,顾分奇偶来讨论。

(为了方便后续的讲解,此时我们比较fa与son编号的大小,如果发现fa编号大于son则交换fa与son,想一想,为什么?)

我们保证fa一定是当前合适的点。

  • 若剩下奇数次,答案是son
  • 若剩下偶数次,答案是fa

其实如果用文字来表示,恕我傻逼,难以表示清楚,但是我们可以举个特殊的例子来方便理解

奇数?1是奇数吧?我接下来再操作一次,一定是操作在fa上,结果就是使得son的权值比fa大一,所以答案是son;

偶数?0是偶数吧?我接下来不操作,我原先最合适的点不是fa吗,所以现在还是fa

证毕.

还有一个小细节,一个可以影响你九十分的小细节,请特判1

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include <bits/stdc++.h>
#define int long long
using namespace std;

inline int read()
{
int s(0), w(1);
char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') w = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s * w;
}

inline void put(int x)
{
if (! x) putchar('0');
if (x < 0) putchar('-'), x = -x;
int num(0); char c[66];
while (x) c[++ num] = x % 10 + 48, x /= 10;
while (num) putchar(c[num --]);
return (void)(putchar('\n'));
}

const int N = 2e6 + 66;

int ver[N << 1], nex[N << 1], head[N], cnt;

inline void add_edge(int x, int y)
{
ver[++ cnt] = y;
nex[cnt] = head[x];
return (void)(head[x] = cnt);
}

int T, n, m, son, fa;
int a[N];

inline void yhm_clear()
{
cnt = fa = son = 0;
memset(a, 0, sizeof a);
memset(nex, 0, sizeof nex);
memset(ver, 0, sizeof ver);
memset(head, 0, sizeof head);
}

inline void yhm_func()
{
int i, x, y;
n = read(), m = read();
for (i = 1; i <= n; ++ i)
{
a[i] = read();
if (a[i] > a[fa]) fa = i;
else if (a[i] == a[fa] && i < fa) fa = i;
}
for (i = 1; i < n; ++ i)
{
x = read(), y = read();
add_edge(x, y), add_edge(y, x);
}
if (n == 1) return (void)(put(1));
for (i = head[fa]; i; i = nex[i])
{
y = ver[i];
if (a[y] > a[son]) son = y;
else if (a[y] == a[son] && y < son) son = y;
}
//cout << "fa-->" << fa << " son-->" << son << '\n'
if (a[fa] - a[son] > m) return (void)(put(fa));
if (a[fa] - a[son] == m) return (void)(put(min(fa, son)));
int rest = m - (a[fa] - a[son]);
if (fa > son) swap(fa, son);
if (rest & 1) return (void)(put(son));
return (void)(put(fa));
}

signed main()
{
T = read();
while (T --)
{
yhm_clear();
yhm_func();
}
return 0;
}