洛谷题解:P6150 [USACO20FEB] Clock Tree S
题意描述
Farmer John 的牛棚有 \(N\) 间房间和 \(N-1\) 条走廊,形成一棵树,每间房间有一个时钟。Bessie 进入房间时,时钟向后拨动一个位置。求有多少个出发房间可以使所有时钟指向 \(12\) 。
解题思路
首先考虑到以下性质:
-
时间从 0 到 12 ,为方便处理,对 12 取模,令 0 表示 12 时刻。
-
对于一个父亲,其儿子的处理顺序不重要。
-
由于在父子来回跳时,一个来回父子各加 1 ,父子之间时间差不变。
于是,设计以下做法:从深处节点开始,通过父子间的跳跃完成一个点的所有子节点时刻转为 0 的操作,而对于这个点剩下的时刻,可通过它和它的父亲节点跳跃完成变为 0 的操作。如此地向上递归。
故,令 \(D_u\) 表示点 \(u\) 此时的时刻,初值为原始时刻。
对于 \(v\) 的父节点 \(u\) 有如下更新:
\(D_u=(12-D_v+D_u) \bmod 12\)
最后来考虑根节点的情况,\(D_{root}\) 记录的是最终回到 \(root\) 点的时刻,当之为 0 时,恰好合格。但是,对于根,我们可以选择不回去,在这种情况下,\(D_{root}\) 的值为 1 。故当 \(D_{root}\) 为0或1时,\(root\) 点合格。
显然,这只是针对一个点的情况,复杂度 \(O(n)\) 。我们枚举每个点,判断是否可行,算法总复杂度为 \(O(n^{2})\) 。
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const int NR = 2510;
int a[NR], d[NR];
//d[]数组即为上文的D。
struct edge
{
int nxt, to;
}e[NR * 2];
int head[NR], cnt;
void add(int x, int y)
{
e[++cnt].nxt = head[x];
e[cnt].to = y;
head[x] = cnt;
}
void dfs(int x, int fa)
{
for (int i = head[x]; i; i = e[i].nxt)
{
int y = e[i].to;
if (y == fa) continue;
dfs(y, x);
d[x] = (12 - d[y] + d[x]) % 12;
}
}
int main()
{
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i ++)
{
scanf("%d", &a[i]);
}
for (int i = 1; i < n; i ++)
{
int x, y;
scanf("%d%d", &x, &y);
add(x, y);
add(y, x);
}
int ans = 0;
for (int i = 1; i <= n; i ++)
{
for (int j = 1; j <= n; j ++) d[j] = a[j];
dfs(i, 0);
if (d[i] == 0 || d[i] == 1) ans ++;
//cout << i << " " << d[i] << endl;
}
cout << ans << endl;
return 0;
}
Further Thinking
在上述情况,不难发现,每次都在使用 \(D\) 数组,所以可以考虑使用换根法优化,这里不再赘述。
咱们不妨通过 \(D\) 的推导来探索另外的做法。
通过上述过程可以知道:(此处忽略对12取模)
\( D_{根} = \ 根节点的初值 \ - \ \sum D_{第2层节点} \\ D_{第2层节点} = \ \sum第2层节点的初值 \ - \ \sum D_{第3层节点} \\ D_{第3层节点} = \ \sum第3层节点的初值 \ - \ \sum D_{第4层节点} \\ \)
从而易得:
\( D_{根} = \ 根节点初值 \ - \ \sum第2层节点的初值 \ + \ \sum第3层节点的初值 \ - \ \sum第4层节点的初值 \ + \ \sum第5层节点的初值 \ - \ ... \)
所以首先对树黑白染色,再枚举每个起点,而这个起点是否可行的依据就是:
与 \(x\) 同色所有点的权值和减去与 \(x\) 异色所有点的权值和得到的这个差对 12 取模后等于 0 或 1 。
Another Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const int NR = 2510;
int a[NR];
int col[NR];
int sum[2];
struct edge
{
int nxt, to;
}e[NR * 2];
int head[NR], cnt;
void add(int x, int y)
{
e[++cnt].nxt = head[x];
e[cnt].to = y;
head[x] = cnt;
}
void dfs(int x, int fa, int k)
{
col[x] = k;
sum[k] += a[x];
for (int i = head[x]; i; i = e[i].nxt)
{
int y = e[i].to;
if (y == fa) continue;
dfs(y, x, k ^ 1);
}
}
int main()
{
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i ++)
{
scanf("%d", &a[i]);
}
for (int i = 1; i < n; i ++)
{
int x, y;
scanf("%d%d", &x, &y);
add(x, y);
add(y, x);
}
dfs(1, 0, 0);
int ans = 0;
for (int x = 1; x <= n; x ++)
{
if (((sum[col[x]] - sum[col[x] ^ 1]) % 12 + 12) % 12 == 0 || ((sum[col[x]] - sum[col[x] ^ 1]) % 12 + 12) % 12 == 1)
{
ans ++;
}
}
cout << ans << endl;
return 0;
}

浙公网安备 33010602011771号