首页 > 其他分享 >启发式合并(树上)

启发式合并(树上)

时间:2023-04-03 21:12:59浏览次数:42  
标签:sz int tr 合并 ri vis 启发式 树上 id

一般用于: 查询每一个子树的相关所有信息, 

内容:

  • dfs,后, 合并儿子的时候, 继承一个重儿子, 然后把其他儿子暴力合进去就行了, 最坏的时间复杂度: Nlong N 
  • 如何继承的时候不是暴力捏? 给 每一个点赋值一个新的ID,就行了. 
  • 重点是在结构体 tr[] 节点里面的各种定义的东西,  来实现我这个代码啥的, 要写相关的函数的(面向对象编程,(和项目比较像了)
  • map,vector 啥的, 动态内存去存.

例题: 

 

 

  •  找到那个深度的父亲利用二进制优化即可
#include <bits/stdc++.h>
using namespace std;
#define ri int 
#define M 200005

int n,m;
int T;
vector <int> p[M];
int vis[M];
int fa[M][32];
void dfs1(int a)
{
    vis[a]=1;
    for(ri i=1;i<=30;i++) fa[a][i]=fa[fa[a][i-1]][i-1];
    
    for(ri b:p[a])
    {
        if(vis[b]) continue;
        dfs1(b);
    }
}
struct tt{
    int id;
    int val;
};
vector <tt> qu[M];
int son[M],dep[M];

struct node{
    map<int,int>num;
    vector<int>lt;
    void add(int a)
    {
        num[dep[a]]++;
        lt.push_back(a);
    }
}tr[M];
int sz[M];
int ans[M];
int id[M];
int cur=0;
void dfs2(int a,int f)
{
    id[a]=++cur;
    dep[a]=dep[f]+1;
    vis[a]=1;
    int mx=0;
    for(ri b:p[a])
    {
        if(vis[b]) continue;
        dfs2(b,a);
        if(sz[b]>mx)
        {
            mx=sz[b];
            son[a]=b;
        }
        sz[a]+=sz[b];
    }
    if(son[a]) id[a]=id[son[a]];
    for(ri b:p[a])
    {
        if(b==f||b==son[a]) continue;
        for(ri v:tr[id[b]].lt) tr[id[a]].add(v);
    }
    for(tt t:qu[a])
    {
        ans[t.id]=max(tr[id[a]].num[dep[a]+t.val]-1,0);
    }
    tr[id[a]].add(a);
    sz[a]++;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    
    cin>>n;
    for(ri i=1;i<=n;i++)
    {
        int a;
        cin>>a;
        fa[i][0]=a;
        if(a==0)
        {
            continue;
        }
        p[a].push_back(i);
    }
    for(ri i=1;i<=n;i++)
    {
        if(fa[i][0]==0)
        {
            dfs1(i);
        }
    }
    cin>>m;
    for(ri i=1;i<=m;i++)
    {
        int a,b;
            
        cin>>a>>b;
            tt t;
            t.id=i;
            t.val=b;
        int cnt=0;
        while(b)
        {
            if(b&1) a=fa[a][cnt];
            b>>=1;cnt++;
        }
        if(a)
        {    
            
            qu[a].push_back(t);
        }
    }
    memset(vis,0,sizeof(vis));
    for(ri i=1;i<=n;i++)
    {
        if(fa[i][0]==0)
        {
            dfs2(i,0);
        }
    }

    for(ri i=1;i<=m;i++) cout<<ans[i]<<" ";
    

} 
View Code

 

标签:sz,int,tr,合并,ri,vis,启发式,树上,id
From: https://www.cnblogs.com/Lamboofhome/p/17284455.html

相关文章

  • 23. 合并 K 个升序链表
    题目描述给你一个链表数组,每个链表都已经按升序排列。请你将所有链表合并到一个升序链表中,返回合并后的链表。解题1/**2*Definitionforsingly-linkedlist.3*publicclassListNode{4*intval;5*ListNodenext;6*ListNo......
  • git 选择合并
    需求:有两个分支,develop,master,需要把develop的提交记录,选择性合并到master1. 将ideal 切换到master分支,checkout 2.   3.根据提交记录,右键cherrypick   4.再执行push操作。合并完成......
  • 将List集合中相同属性的对象合并
    List<User>userList=newArrayList<>();List<User>userMergeList=newArrayList<>();userList.parallelStream().collect(Collectors.groupingBy(o->(o.getUserId()+o.getUserName()),Collectors.toList())).forEach((id,transfer)-&......
  • 石子合并问题
    相邻石子合并问题代码#include<bits/stdc++.h>usingnamespacestd;#definerep(i,a,n)for(inti=a;i<=n;i++)constintN=330;intsum[N];inta[N];intf[N][N];//从i到j合并石子所付出的最小代价intmain(){intn;cin>>n;rep(i,1,n){cin......
  • 23. 合并 K 个升序链表
    23.合并K个升序链表给你一个链表数组,每个链表都已经按升序排列。请你将所有链表合并到一个升序链表中,返回合并后的链表。示例1:输入:lists=[[1,4,5],[1,3,4],[2,6]]输出:[1,1,2,3,4,4,5,6]解释:链表数组如下:[1->4->5,1->3->4,2->6]将它们合并到一个有序链......
  • 21. 合并两个有序链表
     21. 合并两个有序链表做法1:构建虚拟头节点,而后双指针做法。/***Definitionforsingly-linkedlist.*structListNode{*intval;*ListNode*next;*ListNode():val(0),next(nullptr){}*ListNode(intx):val(x),next(nullptr){}......
  • Codeforces Gym 103931F - Forest of Magic(时间轴分块+线段树合并)
    一个巨烦的时间轴分块做法,有点类似于P2137Gty的妹子树先考虑静态的情况。看上去就一脸线段树合并对吧?一次修改的操作对一个点\(x\)贡献可以写成\(k·dep_x+b\)的形式,开两棵线段树合并维护一次项和零次项系数即可。由于静态问题可做,因此考虑时间轴分块。设阈值\(B\),每\(B......
  • 华为OD机试 合并数组
    本期题目:合并数组题目现在有多组整数数组,需要将他们合并成一个新的数组,合并规则:从每个数组里按顺序取出固定长度的内容,合并到新的数组。取完的内容会删除掉,如果该行不足固定长度,或者已经为空,则直接取出剩余部分的内容放到新的数组中继续下一行。输入第1行为每次读取的固......
  • Linux 使用 Split 命令分割文件与合并
    LinuxSplit命令用于将大文件分割成较小的文件(默认每1000行切割成一个小文件),比如在网络质量不佳的情况下需要传输一些较大的视音频文件、程序文件等内容,分割后可以方便我......
  • python字典合并
    #合并字典dic_a={"user":"aa","pwd":"123"}dic_b={"age":12,"sex":"男"}#1.update方法#dic_a.update(dic_b)#print(dic_a)#2.字典解包#dic_new={**dic_a......