P r o b l e m \mathrm{Problem} Problem
S o l u t i o n \mathrm{Solution} Solution
这道题的题目大意是:
- 你需要设定若干个点,使得任意两个点到设定的某一点距离不相同。
这道题的推导比较复杂,我们需要挖掘这道题树的性质:
- 在一个树上,如果我们强制设定某一个点,那么设定所有的叶子节点一定是合法的方案。
如图,例如3和4两种分成两叉的节点,我们可以根据距离叶子节点距离不同这一特点区分;例如2和1这种在某一分叉上面的节点,我们可以通过距离根节点和叶子结点不同这一特点来区分;因此,无论这棵树的形态怎么变,区分的方式只有这两种。 - 根据以上性质,我们可以枚举每一个节点,答案为以该点为根的叶子结点 + 1 +1 +1.
- 同时,这种做法也同样存在着反例。
如图所示,7号点的设定是完全没有必要的,因为该链只需要用到根的距离不同来区分即可。因此,我们发现出现链的情况下,叶子节点是不用设定的。 - 但是,所有的链叶子节点都不用设定吗?
我们发现当链大于等于 2 2 2 条时,两条链的叶节点都不设定的话,便无法区分两条链,因此如果当前节点的子树如果链的条数大于等于1的话,我们便让答案 − 1 -1 −1 即可. - 当然,这么做的前提是,仅在当前某点为根的子树内。因此在实际的操作上,我们统计子树全部完成了覆盖的方案,在根据是否存在一条完整的链来进行减一操作即可,该操作与子树是否完成了减一操作无关。
C o d e \mathrm{Code} Code
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5;
int n, res(0), k;
int val[N];
vector < int > a[N];
int read(void)
{
int s = 0, w = 0; char c = getchar();
while (c < '0' || c > '9') w |= c == '-', c = getchar();
while (c >= '0' && c <= '9') s = s*10+c-48, c = getchar();
return w ? -s : s;
}
void Dfs2(int x,int fa,int Max,int sum)
{
if (sum > Max) res += ((sum - Max) % k == 0);
for (int i=0;i<a[x].size();++i)
{
int y = a[x][i];
if (y == fa) continue;
Dfs2(y,x,max(Max,val[y]),sum+val[y]);
}
return;
}
int main(void)
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
n = read(), k = read();
for (int i=1;i<n;++i)
{
int x = read(), y = read();
a[x].push_back(y);
a[y].push_back(x);
}
for (int i=1;i<=n;++i) val[i] = read();
for (int i=1;i<=n;++i)
Dfs2(i,0,val[i],val[i]);
cout << res / 2 + n << endl;
return 0;
}