zoukankan      html  css  js  c++  java
  • [BZOJ 4719] 天天爱跑步

    Link:

    BZOJ 4719 传送门

    Solution:

    感觉求LCA又有了新姿势啊:$Tarjan$离线$O(n+m)$

    每次递归返回时将子树和父节点合并,如果询问节点已访问过则LCA就是已合并的最高节点

    这题部分分提示非常多啊

    首先要将路径拆为$(S,LCA),(LCA,T)$

    发现如果$(S,LCA)$能对点$x$产生贡献要满足$w[x]+dep[x]=dep[S]$

    而$(LCA,T)$能对点$x$产生贡献要满足$dep[x]-w[x]=dep[T]-len$

    这样用$cnt$数组维护等式右边的$dep[S]$和$dep[T]-len$的值有多少个就能快速得出有几条路径满足条件

    于是可以在路径起点加入该路径特征值并在路径末尾将其消除即可

    注意:

    1、$LCA$处可能算了两遍,最后要逐一判断

    2、要在刚进入该点时记录当前$cnt[w[x]+dep[x]]$的值否则可能会将其它子树中未走完的路径计算在内

    3、此题需要从下往上统计答案,因此路径起点都要设置为深度较大的,否则不好消除不经过该点路径的贡献

    Code:

    #include <bits/stdc++.h>
    
    using namespace std;
    #define X first
    #define Y second
    #define pb push_back
    typedef double db;
    typedef long long ll;
    typedef pair<int,int> P;
    const int MAXN=1e6+10,ADD=3e5;
    int vis[MAXN],f[MAXN],st[MAXN];
    int n,q,x,y,w[MAXN],res[MAXN],cnt[MAXN],head[MAXN],dep[MAXN],tot;
    
    vector<P> par[MAXN];
    vector<int> in[MAXN],out[MAXN];
    struct edge{int nxt,to;}e[MAXN<<2];
    struct Query{int x,y,lca;}qry[MAXN];
    
    void add(int x,int y)
    {e[++tot]=(edge){head[x],y};head[x]=tot;}
    int find(int x)
    {return f[x]==x?x:f[x]=find(f[x]);}
    void tarjan(int x,int anc)
    {
        vis[x]=1;f[x]=x;
        for(int i=0;i<par[x].size();i++)
            if(vis[par[x][i].X]) qry[par[x][i].Y].lca=find(par[x][i].X);
        for(int i=head[x];i;i=e[i].nxt)
            if(e[i].to!=anc)
            {
                dep[e[i].to]=dep[x]+1;
                tarjan(e[i].to,x);f[e[i].to]=x;
            }
    }
    void dfs1(int x,int anc)
    {
        int cur=cnt[w[x]+dep[x]];
        for(int i=head[x];i;i=e[i].nxt)
            if(e[i].to!=anc) dfs1(e[i].to,x);
        cnt[dep[x]]+=st[x];
        res[x]+=cnt[w[x]+dep[x]]-cur;
        for(int i=0;i<out[x].size();i++) cnt[out[x][i]]--;
    }
    void dfs2(int x,int anc)
    {
        int cur=cnt[ADD-w[x]+dep[x]];
        for(int i=head[x];i;i=e[i].nxt)
            if(e[i].to!=anc) dfs2(e[i].to,x);
        //都要看成从底向上的路径 
        for(int i=0;i<in[x].size();i++) cnt[in[x][i]]++;
        res[x]+=cnt[ADD-w[x]+dep[x]]-cur;
        for(int i=0;i<out[x].size();i++) cnt[out[x][i]]--;
    }
    
    int main()
    {
        scanf("%d%d",&n,&q);
        for(int i=1;i<n;i++)
            scanf("%d%d",&x,&y),add(x,y),add(y,x);
        for(int i=1;i<=n;i++) scanf("%d",&w[i]);
        for(int i=1;i<=q;i++) 
        {
            scanf("%d%d",&x,&y);
            qry[i].x=x,qry[i].y=y;
            par[x].pb(P(y,i));par[y].pb(P(x,i));
        }
        tarjan(1,0);
        
        for(int i=1;i<=q;i++)
            out[qry[i].lca].pb(dep[qry[i].x]),st[qry[i].x]++;
        dfs1(1,0);
        memset(cnt,0,sizeof(cnt));
        for(int i=1;i<=n;i++) out[i].clear();
        for(int i=1;i<=q;i++)
        {
            int len=dep[qry[i].x]+dep[qry[i].y]-2*dep[qry[i].lca];
            in[qry[i].y].pb(ADD+dep[qry[i].y]-len);
            out[qry[i].lca].pb(ADD+dep[qry[i].y]-len);
        }
        dfs2(1,0);
        for(int i=1;i<=q;i++)
            if(dep[qry[i].x]-dep[qry[i].lca]==w[qry[i].lca]) 
                res[qry[i].lca]--;
        for(int i=1;i<=n;i++)
            printf("%d ",res[i]);
        return 0;
    }
  • 相关阅读:
    C#终止线程的方法
    Socket编程(TCP、UDP)
    频率域滤波基础之五(读数字图像处理学习halcon)
    hihoCoder #1127:二分图最小点覆盖和最大独立集
    hihoCoder #1033 : 交错和 (数位Dp)
    HDU-5536 Chip Factory (字典树)
    hihoCoder #1040 (判断是否为矩形)
    hihoCoder:#1079(线段树+离散化)
    HDU-5532 Almost Sorted Array (LIS)
    UVALive-7303 Aquarium (最小生成树)
  • 原文地址:https://www.cnblogs.com/newera/p/9718233.html
Copyright © 2011-2022 走看看