首页 > 其他分享 >题解 P4183 [USACO18JAN] Cow at Large P

题解 P4183 [USACO18JAN] Cow at Large P

时间:2023-07-17 23:13:13浏览次数:52  
标签:rt limits int 题解 sum son Large P4183 dis

带有小 trick 的点分治。

建议先做完 弱化版 再看。

假如奶牛在 \(u\),那么所需的最少农夫数为 \(\sum\limits_{v\in son(u)}[dis(u,v)\ge g_v][dis(u,fa_v)<g_{fa_v}]\)。

其中 \(dis(u,v)\) 为 \(u,v\) 在树上的距离,\(g_u\) 为 \(u\) 到离它最近的出入口的距离(BFS 预处理),\(fa_u\) 为 \(u\) 的父亲。

复杂度 \(O(n^2)\)。

由于 \(dis(u,fa_v)<g_{fa_v}\) 限制的存在,不好优化。

但通过人类智慧我们发现,对于 \(u\) 这棵子树,\(\sum\limits_{v\in son(u)}in_v=2n-1\)。其中 \(in_u\) 为 \(u\) 的度数,\(n\) 为子树大小,\(v\) 可以为 \(u\)。

将式子变换一下得:\(1=\sum\limits_{v\in son(u)}(2-in_v)\)。

这样原式就可以去掉后面的限制,变为 \(\sum\limits_{v\in son(u)}[dis(u,v)\le g_v](2-in_v)\)。

接下来点分治的操作和 这道题 很像,可以看一眼。

具体来说,对于分治重心 \(rt\) 中的两个点 \(u,v\),当前仅当 \(g_u\le d_u+d_v\) 时,\(v\) 会对 \(u\) 产生贡献。其中 \(d_u\) 为 \(dis(u,rt)\)。这其实就是个点对问题。

所以我们可以以 \(g_u-d_u\) 为下标建一棵树状数组,每次访问前缀和即可。由于下标可能为负,所以需要整体平移一下。

复杂度 \(O(n\log^2 n)\)。

code:

#include<bits/stdc++.h>
using namespace std;
const int N=7e4+5;
int n,g[N],vis[N],f[N],siz[N],d[N],c[N*2],in[N],tot,ans[N],t[N],cnt,rt;
queue<int>q;
vector<int>adj[N];
int lbt(int x){return x&(-x);}
void update(int i,int k){for(;i<=2*n;i+=lbt(i))c[i]+=k;}
int getsum(int i){int res=0;for(;i;i-=lbt(i))res+=c[i];return res;}
void dfs(int u,int lst){
  siz[u]=1;f[u]=0;
  for(int i=0;i<adj[u].size();++i){
    int v=adj[u][i];if(v==lst||vis[v])continue;
    dfs(v,u);siz[u]+=siz[v];f[u]=max(f[u],siz[v]);
  }
  f[u]=max(f[u],tot-siz[u]);
  if(f[u]<f[rt])rt=u;
}
void getdis(int u,int lst){
  t[++cnt]=u;
  for(int i=0;i<adj[u].size();++i){
    int v=adj[u][i];if(v==lst||vis[v])continue;
    d[v]=d[u]+1;getdis(v,u);
  }
}
void getans(int u,int len,int op){
  d[u]=len;cnt=0;getdis(u,0);
  for(int i=1;i<=cnt;++i)update(g[t[i]]-d[t[i]]+n,2-in[t[i]]);
  for(int i=1;i<=cnt;++i){
    update(g[t[i]]-d[t[i]]+n,in[t[i]]-2);
    ans[t[i]]+=op*getsum(d[t[i]]+n);
    update(g[t[i]]-d[t[i]]+n,2-in[t[i]]);
  }
  for(int i=1;i<=cnt;++i)update(g[t[i]]-d[t[i]]+n,in[t[i]]-2);
}
void solve(int u){
  getans(u,0,1);vis[u]=1;
  for(int i=0;i<adj[u].size();++i){
    int v=adj[u][i];if(vis[v])continue;
    getans(v,1,-1);
    rt=0;tot=siz[v];dfs(v,u);solve(rt);
  }
}
int main(){
  ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
  cin>>n;
  for(int i=1;i<n;++i){
    int u,v;cin>>u>>v;
    adj[u].push_back(v);adj[v].push_back(u);++in[u];++in[v];
  }
  for(int i=1;i<=n;++i)if(in[i]==1)q.push(i),vis[i]=1;
  while(!q.empty()){
    int u=q.front();q.pop();
    for(int i=0;i<adj[u].size();++i){
      int v=adj[u][i];if(vis[v])continue;
      vis[v]=1;g[v]=g[u]+1;q.push(v);
    }
  }
  for(int i=1;i<=n;++i)vis[i]=0;
  f[0]=1e9;tot=n;dfs(1,0);solve(rt);
  for(int i=1;i<=n;++i)cout<<ans[i]<<endl;
  return 0;
}

标签:rt,limits,int,题解,sum,son,Large,P4183,dis
From: https://www.cnblogs.com/HQJ2007/p/17561560.html

相关文章

  • 题解 P9415 下楼
    不难推理出当初始绳长为\(len\),需要下降的距离为\(d\),并且满足\(d\lelen<2d\)时,最优的环套法可以只损失\(2d-len\)长度的绳子,留下\(2(len-d)\)的绳子。当\(2d\lelen\)时存在一种不损耗绳长的方法可以下到下一根钢管,即把整根绳子系成一个环,到达下面再剪断。正文:线......
  • 题解 P9437『XYGOI round1』一棵树
    换根DP。本蒟蒻最初没想到换根,把自己写自闭了...定义\(f_u\)为子树\(u\)中的每个结点走到\(u\)的贡献和,\(l_u\)为大于\(a_u\)的最小的\(10\)的幂次方数,\(sum_u\)为\(\sum\limits_{v\inson(u)}{f_v}\)。转移方程为:\(f_u=l_u\cdot\sum\limits_{v\inson(u)}{f_v}+......
  • 决策单调性优化DP 学习笔记 & P4767 [IOI2000] 邮局 题解
    0.题面题目描述高速公路旁边有一些村庄。高速公路表示为整数轴,每个村庄的位置用单个整数坐标标识。没有两个在同样地方的村庄。两个位置之间的距离是其整数坐标差的绝对值。邮局将建在一些,但不一定是所有的村庄中。为了建立邮局,应选择他们建造的位置,使每个村庄与其最近的邮局......
  • 题解 P7215
    点分治。考虑当前的分治重心的城市被完全联通。这可以用队列接解决。每次放入一种城市,就把那些城镇的父亲加入队列,如果存在城镇不在当前分治重心的联通块内,那么说明必定存在另一个分治重心能算到它,直接退出即可。剩下的和模板没有任何区别。复杂度\(O(n\logn)\)。code:#in......
  • 题解 P6329
    点分树模板题。是个神奇的算法。点分树就是将分治重心按照层级连边,每个节点父亲的联通块都包含了当前节点的联通块,且高度为\(\logn\)。可以解决不考虑树的形态的多次询问带修改的问题。对于这道题,我们可以在点分树的每个节点上记录两棵树状数组,分别与当前节点距离为\(k\)的......
  • 题解 P3345
    点分树。本题的核心问题在于不好直接确定补给站的位置。但是仔细思考后我们发现,对于当前节点,如果存在一个儿子的答案比它优,那么补给站一定在那个儿子的子树中。增量为\(w\times(sum_u-2\cdotsum_v)\)。当\(v\)优于\(u\)时,\(2\cdotsum_v>sum_u\)。如果\(v_2\)也满足,则......
  • 题解 P5384
    这题有紫??对于询问节点\(u\),倍增地跳到它的\(k\)级祖先,求其子树内有多少深度为\(dep_u\)的节点。我们考虑把询问离线,再通过dfs序把树拍平,然后扫一遍直接求就行了。复杂度\(O(n\logn)\)。code:#include<bits/stdc++.h>usingnamespacestd;constintN=1e6+5;inlin......
  • 题解 P3806
    点分治模板题。点分治适合处理大规模的树上路径信息问题暴力做法:dfs每个点\(u\),算出其子树内每个点到\(u\)的距离,统计经过\(u\)的所有路径,复杂度\(O(n^2)\)。容易发现,复杂度和子树大小有关。对于当前子树,我们可以求出其重心,计算经过重心的所有路径,删掉重心,递归每个联通......
  • 题解 CF1271D
    贪心+DP。对于一个点,后选显然比先选好,也就是说每个点都对应了唯一一个来源。于是我们可以把每个点所能回溯到的点的收益值从大到小排序,贪心地选前缀。定义\(f_{i,j}\)表示考虑了前\(i\)个点,剩下\(j\)个人,最大收益。转移方程和\(01\)背包的一样。\[f_{i,j}=f_{i-1,j-b......
  • 题解 CF213C
    考虑朴素DP。定义\(f_{i,j,i2,j2}\)表示两个人分别在\((i,j),(i2,j2)\)时获得的最大收益。复杂度\(O(n^4)\),不行。我们换种方法,定义\(f_{st,x,y}\)表示两人同时走了\(st\)步,分别向右走了\(x,y\)步。显然如果向右的步数确定了,向下的也确定了。转移方程如下:\[f_{st,x......