ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[题解]由一个平凡的结论引出的两道树上趣题

2021-11-30 17:32:18  阅读:145  来源: 互联网

标签:rs int 题解 mn tr 趣题 ls 平凡 mx


现在要求一个树上点集生成树的边数。
很显然,我们把点按 DFS 序排序后就是相邻两个点的距离加起来除以二了,因为 DFS 的时候每条边恰好走了两遍。
然后你就可以解决 SDOI2015 寻宝游戏了,只需要用 std::set 维护宝藏点,然后查询前驱后继计算距离增量即可。

一个麻烦一点的题:ZJOI2019 语言。
稍微手玩一下就可以发现我们钦定一个端点后就变成了分别求经过每个点的路径并的大小,也就是一条路径会造成其上所有点对应的生成树多出来两个端点,所以我们用类似雨天的尾巴的做法,对路径树上差分再线段树合并。容易发现上面那个 DFS 序排序后的距离和很容易搬到线段树上维护,只需要多记录每个节点的最小和最大 DFS 序,合并节点时计算上增加的距离即可。
注意本题可能卡常,建议使用 \(O(n\log n)-O(1)\) LCA。
此处为方便理解给出线段树和主函数部分代码。注意由于我们钦定了一个端点,实际求得的是无序对,答案需要除以二:

struct Node {
  int l,r,mn,mx,w,cnt;
}tr[N*50];
int rt[N],tot,bin[N*50],tp;
il void Pushup(int k){
  tr[k].mn=tr[ls].mn?tr[ls].mn:tr[rs].mn;
  tr[k].mx=tr[rs].mx?tr[rs].mx:tr[ls].mx;
  tr[k].w=tr[ls].w+tr[rs].w;
  if(tr[ls].mx&&tr[rs].mn){
    tr[k].w+=Dis(id[tr[ls].mx],id[tr[rs].mn]);
  }
}
void Modify(int &k,int pos,int v,int l=1,int r=tim){
  if(!k)k=tp?bin[tp--]:++tot;
  if(l==r){
    tr[k].cnt+=v,tr[k].w=0;
    tr[k].mn=tr[k].mx=tr[k].cnt?l:0;return;
  }
  if(pos<=nmid)Modify(ls,pos,v,l,nmid);
  else Modify(rs,pos,v,nmid+1,r);
  Pushup(k);
}
int Merge(int u,int v,int l=1,int r=tim){
  if(!u||!v)return u+v;
  if(l==r){
    tr[u].cnt+=tr[v].cnt,tr[u].mn=tr[u].mx=tr[u].cnt?l:0;
    return u;
  }
  tr[u].l=Merge(tr[u].l,tr[v].l,l,nmid);
  tr[u].r=Merge(tr[u].r,tr[v].r,nmid+1,r);
  bin[++tp]=v,tr[v].mn=tr[v].mx=tr[v].w=tr[v].cnt=0;
  tr[v].l=tr[v].r=0;
  return Pushup(u),u;
}
LL ans;
void Calc(int u){
  for(int i=hd[u];i;i=e[i].nxt){
    int v=e[i].to;
    if(v!=f[0][u])Calc(v),rt[u]=Merge(rt[u],rt[v]);
  }
  int r=rt[u];
  if(tr[r].mn&&tr[r].mx&&tr[r].mn!=tr[r].mx){
    ans+=tr[r].w+Dis(id[tr[r].mn],id[tr[r].mx]);
  }
}
signed main(){
  Read(n),Read(m);
  for(int i=1,u,v;i<n;i++){
    Read(u),Read(v),ade(u,v),ade(v,u);
  }
  DFS(1,0),Init();
  for(int i=1,u,v,l;i<=m;i++){
    Read(u),Read(v),l=LCA(u,v);int dfu=dfn[u],dfv=dfn[v];
    Modify(rt[u],dfu,1),Modify(rt[v],dfu,1);
    Modify(rt[u],dfv,1),Modify(rt[v],dfv,1);
    Modify(rt[l],dfu,-1),Modify(rt[l],dfv,-1);
    if(f[0][l])Modify(rt[f[0][l]],dfu,-1);
    if(f[0][l])Modify(rt[f[0][l]],dfv,-1);
  }
  Calc(1),printf("%lld\n",ans>>2);
  KafuuChino HotoKokoa
}

这两道题都很简单,考察的主要是选手对于简单结论的应用。

标签:rs,int,题解,mn,tr,趣题,ls,平凡,mx
来源: https://www.cnblogs.com/juruoajh/p/15625155.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有