zoukankan      html  css  js  c++  java
  • [题解]由一个平凡的结论引出的两道树上趣题

    现在要求一个树上点集生成树的边数。
    很显然,我们把点按 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
    }
    

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

    内容来自_ajthreac_的博客(https://www.cnblogs.com/juruoajh/),未经允许,不得转载。
  • 相关阅读:
    养成好习惯:在控制面板里停止服务
    很吊炸天的Xcode插件,你想要的这都有
    Cscope how to support java and c++
    java_lambda表达式
    【C语言】14-返回指针的函数与指向函数的指针
    李洪强
    【C语言】13-指针和字符串
    李洪强-C语言7-C语言运算符
    【C语言】12-指向一维数组元素的指针
    李洪强-C语言6-控制结构
  • 原文地址:https://www.cnblogs.com/juruoajh/p/15625155.html
Copyright © 2011-2022 走看看