zoukankan      html  css  js  c++  java
  • [BZOJ 3631] 松鼠的新家

    Link:

    BZOJ 3631 传送门

    Solution:

    这题一眼看上去是裸的树剖,但实际上完全没有必要进行区间加的操作

    由于不需要在线的查询,我们可以直接用差分数组来解决此题

    而这又有两种方式:

    1、先轻重链剖分,按树剖更新时一样处理差分数组

    对于每一条连续的部分执行$res[pos[top[x]]]++$和$res[pos[x]+1]--$

    注意除去每个点的重复计算和对起点及终点的特殊处理!

    2、直接对$res[dat[i]]++$和$res[dat[i+1]]++$,$res[lca]--$和$res[father[lca]]--$

    最后从底至上进行更新即可

    注意$LCA$处重复算了一次,要减一,而$father[LCA]$还要再减一才能满足差分

    二者复杂度差不多,但(2)好像更妙一点

    Solution1:

    #include <bits/stdc++.h>
    
    using namespace std;
    const int MAXN=3e5+10;
    int siz[MAXN],top[MAXN],dep[MAXN],pos[MAXN],f[MAXN][20],dat[MAXN],n,res[MAXN],cnt=0;
    vector<int> G[MAXN]; 
    
    void dfs1(int k,int anc) 
    {
        siz[k]=1;
        for(int i=1;(1<<i)<=dep[k];i++)
            f[k][i]=f[f[k][i-1]][i-1];
        for(int i=0;i<G[k].size();i++)
        {
            int v=G[k][i];
            if(anc==v) continue;
            f[v][0]=k;dep[v]=dep[k]+1;
            dfs1(v,k);siz[k]+=siz[v];
        }
    }
    
    void dfs2(int k,int up) //树剖的处理
    {
        int bs=0;pos[k]=++cnt;top[k]=up;
        for(int i=0;i<G[k].size();i++)
            if(G[k][i]!=f[k][0] && siz[G[k][i]]>siz[bs]) bs=G[k][i];
        if(!bs) return;
        
        dfs2(bs,up);
        for(int i=0;i<G[k].size();i++)
            if(G[k][i]!=f[k][0] && G[k][i]!=bs) dfs2(G[k][i],G[k][i]);
    }
    
    void LCA(int x,int y)
    {
        while(top[x]!=top[y])
        {
            if(dep[top[x]]<dep[top[y]]) swap(x,y);
            res[pos[top[x]]]++;res[pos[x]+1]--;x=f[top[x]][0];
        }
        if(dep[x]<dep[y]) swap(x,y);
        res[pos[y]]++;res[pos[x]+1]--;
    }
    
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&dat[i]);
        for(int i=1;i<n;i++)
        {
            int x,y;scanf("%d%d",&x,&y);
            G[x].push_back(y);G[y].push_back(x);
        }
        
        dfs1(1,0);dfs2(1,1);
        
        for(int i=1;i<=n;i++)
        {
            if(i==1){res[pos[dat[i]]]++;res[pos[dat[i]]+1]--;continue;}        
            LCA(dat[i-1],dat[i]);res[pos[dat[i-1]]]--;res[pos[dat[i-1]]+1]++;  //减去对每个点重复算的一次
            if(i==n){res[pos[dat[i]]]--;res[pos[dat[i]]+1]++;}
        }
        for(int i=1;i<=cnt;i++) res[i]+=res[i-1];
        for(int i=1;i<=n;i++) printf("%d
    ",res[pos[i]]);
        return 0;
    }

    Solution2:

    #include <bits/stdc++.h>
    
    using namespace std;
    const int MAXN=3e5+10;
    int siz[MAXN],dep[MAXN],f[MAXN][20],dat[MAXN],n,res[MAXN];
    vector<int> G[MAXN]; 
    
    void dfs(int k,int anc)
    {
        for(int i=1;(1<<i)<=dep[k];i++)
            f[k][i]=f[f[k][i-1]][i-1];
        for(int i=0;i<G[k].size();i++)
        {
            int v=G[k][i];
            if(anc==v) continue;
            f[v][0]=k;dep[v]=dep[k]+1;dfs(v,k);
        }
    }
    
    int LCA(int x,int y)
    {
        if(dep[x]<dep[y]) swap(x,y);
        int t=dep[x]-dep[y];
        for(int i=0;i<=18;i++)
            if(t&(1<<i)) x=f[x][i];
        for(int i=18;i>=0;i--)
            if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
        if(x==y) return x;
        return f[x][0];
    }
    
    void cnt(int k)  //从底至上更新
    {
        for(int i=0;i<G[k].size();i++)
        {
            int v=G[k][i];
            if(v==f[k][0]) continue;
            cnt(v);res[k]+=res[v];
        }
    }
    
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&dat[i]);
        for(int i=1;i<n;i++)
        {
            int x,y;scanf("%d%d",&x,&y);
            G[x].push_back(y);G[y].push_back(x);
        }
        
        dfs(dat[1],0);
        for(int i=2;i<=n;i++)
        {
            int lca=LCA(dat[i-1],dat[i]);
            res[dat[i]]++;res[dat[i-1]]++;
            res[lca]--;res[f[lca][0]]--;
        }
        cnt(dat[1]);
        for(int i=2;i<=n;i++) res[dat[i]]--; //减去重复的次数
        for(int i=1;i<=n;i++) printf("%d
    ",res[i]);
        return 0;
    }

    Review:

    这题竟然是第一次写树上差分,

    如果仅仅是两点间修改,而没有在线查询的话,直接差分的常数是要比区间加的效率高得多的

    因此能用差分还是用差分

    记住如果不是树剖的差分是从底至上更新

  • 相关阅读:
    opencv源码编译安装后使用时出现undefined reference cv::imwrite
    OPPO手机永久打开USB调试模式
    bash 顺序执行等待前一个脚本执行完成
    cpp
    多线程
    关于nvme下ubuntu无法识别硬盘的问题
    极限建站
    新生赛
    pc_mbed_fpga_communication
    color_sensor_mbed
  • 原文地址:https://www.cnblogs.com/newera/p/9119306.html
Copyright © 2011-2022 走看看