P4183 [USACO18JAN]Cow at Large P

题目链接

要考虑子树不要光顾着想对于一个叶子的情况啊!!

暴力

考虑贝西所在节点固定时怎么做。我们以贝西为根节点,如果某个农夫能从叶节点迅速控制某个子树的根节点,那么这棵子树就只用一个农夫,否则是其儿子的农夫数之和。用式子表示就是:设 \(f_x\) 表示 \(x\) 子树所需要农夫的最少数量,\(g_x\) 表示能到达 \(x\) 的最近叶节点的距离,那么有:

\[f_x = 1,dep_x \ge g_x \]

\[f_x = \sum_yf_y,dep_x < g_x \]

树形DP即可。复杂度 \(O(n^2)\)

正解

考虑如何优化暴力。感觉不是很好优化的样子。我们不去管 \(f_x\) 具体是多少了,我们只去管最终能对答案贡献 1 的子树。我们发现 \(dep_x \ge g_x\) 这个条件有个特点,满足这个条件的一定是一棵棵子树。因此我们可以在子树的顶点上把这个 1 加上。

发现还是不好做。考虑树形差分(好像叫这个),一棵子树(整棵树除外)的点的度数的和为 \(2siz - 1\),即:

\[\sum_{i}d_i = 2siz - 1 \]

移项:

\[\sum_{i}2 - d_i = 1 \]

这样,如果每个点的贡献是 \(2 - d_i\) 的话,整棵子树的贡献就是 1.(神奇)

这样,问题就转化为了:

\[ans_p = \sum_{x}(2 - d_x)[dis(x,p) \ge g_x] \]

发现这个可以看作点对 \((x, p)\) 之间的关系,因此可以点分治做。加在 \(g_x - dep_x\),查 \(\le dep_p\) 的贡献和。

关键代码:

int d[N];
int g[N];
void dfs_dp(int cur, int faa) {
  if (d[cur] == 1)  g[cur] = 0;
  else  g[cur] = n;
  for (register int i = head[cur]; i; i = e[i].nxt) {
    int to = e[i].to; if (to == faa)  continue;
    dfs_dp(to, cur); MIN(g[cur], g[to] + 1);
  }
}
void dp_dfs(int cur, int faa) {
  for (register int i = head[cur]; i; i = e[i].nxt) {
    int to = e[i].to; if (to == faa)  continue;
    MIN(g[to], g[cur] + 1); dp_dfs(to, cur);
  }
}

int siz[N], Siz, mxSiz, root;
bool vis[N];
void find_root(int cur, int faa)
uint ans[N];
void dfs_calc(int cur, int faa, int nwd) {
  ans[cur] += query(nwd);
  for (register int i = head[cur]; i; i = e[i].nxt) {
    int to = e[i].to; if (to == faa || vis[to]) continue;
    dfs_calc(to, cur, nwd + 1);
  }
}
void dfs_add(int cur, int faa, int nwd) {
  add(g[cur] - nwd, 2 - d[cur]);
  for (register int i = head[cur]; i; i = e[i].nxt) {
    int to = e[i].to; if (to == faa || vis[to]) continue;
    dfs_add(to, cur, nwd + 1);
  }
}
void dfs_del(int cur, int faa, int nwd) {
  del(g[cur] - nwd);
  for (register int i = head[cur]; i; i = e[i].nxt) {
    int to = e[i].to; if (to == faa || vis[to]) continue;
    dfs_del(to, cur, nwd + 1);
  }
}

int stk[N], stop;
void dfs(int cur) {
  for (register int i = head[cur]; i; i = e[i].nxt) {
    int to = e[i].to; if (vis[to])  continue;
    dfs_calc(to, cur, 1);
    dfs_add(to, cur, 1); stk[++stop] = to;
  }
  ans[cur] += query(0);
  dfs_del(cur, 0, 0);
  add(g[cur], 2 - d[cur]);
  for (register int i = stop; i; --i) {
    int to = stk[i]; dfs_calc(to, cur, 1); dfs_add(to, cur, 1);
  }
  stop = 0;
  dfs_del(cur, 0, 0);
  vis[cur] = true;
  for (register int i= head[cur]; i; i = e[i].nxt) {
    int to = e[i].to; if (vis[to])  continue;
    Siz = siz[to], mxSiz = n;
    find_root(to, cur);
    dfs(root);
  }
}

int main() {
  read(n); nn = n + n;
  for (register int i = 1; i < n; ++i) {
    int u, v; read(u), read(v);
    addedge(u, v), addedge(v, u);
    ++d[u]; ++d[v];
  }
  dfs_dp(1, 0); dp_dfs(1, 0);
  Siz = mxSiz = n;
  find_root(1, 0);
  dfs(root);
  for (register int i = 1; i <= n; ++i) {
    if (d[i] == 1)  ans[i] = 1;
    Print(ans[i]);
  }
  return 0;
}
posted @ 2020-08-30 22:04  JiaZP  阅读(106)  评论(0编辑  收藏  举报