zoukankan      html  css  js  c++  java
  • 启发式合并

    推荐博客 : https://blog.csdn.net/zjznku/article/details/65937416

    其实启发式合并核心思想就是将小的集合合并到大的集合上面

    例题 : HDU 4358

    求一颗树上以每个点为子树中权值出现 k 次的个数有多少个

    #include <bits/stdc++.h>
    using namespace std;
    #define ll long long
    const int maxn = 1e5+5;
    
    int n, k;
    int a[maxn];
    vector<int>ve[maxn], vv;
    int size[maxn], son[maxn];
    int que[maxn];
    
    void init(){
        vv.clear();
        for(int i = 1; i <= 100000; i++) ve[i].clear();
        memset(size, 0, sizeof(size));
        memset(son, 0, sizeof(son));
    }
    
    
    void dfs1(int x){ //求出每个结点的重儿子
        size[x] = 1;
        
        int num = 0;
        for(int i = 0; i < ve[x].size(); i++){
            int to = ve[x][i];
            dfs1(to);
            size[x] += size[to];
            if (size[to] > num) {num = size[to]; son[x] = to;}
        }
    }
    int sum[maxn], ans[maxn];
    int res = 0;
    bool vis[maxn];
     
    void edit(int x, int sp){  //更新每个点的贡献
        if (sum[a[x]] == k) res--;
        sum[a[x]] += sp;
        if (sum[a[x]] == k) res++;
        
        for(int i = 0; i < ve[x].size(); i++){
            int to = ve[x][i];
            if (vis[to]) continue;
            edit(to, sp);
        }
    }
    void dfs(int x, int sp){    //sp表示贡献是否被清空
        for(int i = 0; i < ve[x].size(); i++){ //遍历所有非重儿子的结点
            int to = ve[x][i];
            if (to == son[x]) continue;
            dfs(to, 0);
        }
        if (son[x]) dfs(son[x], 1), vis[son[x]] = 1;
        edit(x, 1); //遍历x的所有结点 添加重儿子以外的贡献
        ans[x] = res;  //记录答案
        
        if (son[x]) vis[son[x]] = 0; //重儿子的标记取消
        if (!sp) edit(x, -1); //贡献取消
    }
    
    int main() {
        //freopen("in.txt", "r", stdin);
        //freopen("out.txt", "w", stdout);
        
        int T;
        cin >> T;
        int x, y;
        int kas = 1;
        int sign = 0;
        
        while(T--) {
            if (sign) printf("
    ");
            init();
            scanf("%d%d", &n, &k);
            for(int i = 1; i <= n; i++) {
                scanf("%d", &a[i]);
                vv.push_back(a[i]);
            }        
            sort(vv.begin(), vv.end());
            vv.erase(unique(vv.begin(), vv.end()), vv.end());
            for(int i = 1; i <= n; i++) a[i] = lower_bound(vv.begin(), vv.end(), a[i])-vv.begin()+1;
            
             
            for(int i = 1; i < n; i++){
                scanf("%d%d", &x, &y);
                ve[x].push_back(y);     
            }
            dfs1(1); dfs(1, 0);
            int q;
            
            cin >> q;
            for(int i = 1; i <= q; i++) {scanf("%d", &que[i]);}
            printf("Case #%d:
    ", kas++);
            for(int i = 1; i <= n; i++) printf("%d
    ", ans[que[i]]);
            sign = 1; 
            //for(int i = 1; i <= n; i++) printf("%d ", sum[i]);
        }
        return 0;
    }
    
    东北日出西边雨 道是无情却有情
  • 相关阅读:
    linux 操作系统/xxx目录下都是什么文件?
    Linux /bin, /sbin, /usr/bin, /usr/sbin 区别
    java初学
    虚拟机联网及远程连接-Linux基础环境命令学习笔记
    Linux 文件操作命令-Linux基础环境命令学习笔记
    Linux 权限、磁盘操作命令-Linux基础环境命令学习笔记
    Linux shell编程命令-Linux基础环境命令学习笔记
    C程序编译执行过程
    刨根问底:什么是yum源,yum的工作原理又是什么
    网关人性化详解
  • 原文地址:https://www.cnblogs.com/ccut-ry/p/9689234.html
Copyright © 2011-2022 走看看