zoukankan      html  css  js  c++  java
  • CF516D

    CF516D

    给定一棵 n 个点的树,边有边权。
    定义一个点的权值为 :该点距离树上所有点的距离中,最大的那个距离
    q 次询问最大的满足 所有点的最大权值减最小权值 小于等于 x 的连通块 s 包含的点数。
    n ≤ 1e5,q ≤ 50。

    ====================================================================

    首先,离每个点最远的点一定是直径的两个端点之一,我们可以因此求出点权

    然后我们发现,点权最小的点,一定是最靠近直径中点的点(如果有中点那么就是中点了)。如果以这个点为根建一棵树,我们就能得到一个随深度递增,权值也递增的树。

    那么求连通块时,我们先找一个下边界点,也就是该连通块中点权最大的那个,然后看看往上能到哪个祖先节点,再利用查分的思想给这一段都 + 1 ,最大值就是答案

    code

    //对不住了,代码有点长,压个行qwq 
    typedef pair <long long,int> PLI;
    vector <PLI> g;
     
    void dfs(int x,int p,int ok){for(int i = head[x];i;i = nxt[i]){int y = to[i];if(y == p) continue;if(ok == 1) dis_a[y] = dis_a[x] + w[i];else dis_b[y] = dis_b[x] + w[i];dfs(y,x,ok);}}
    int get(){int j = 0;for(int i = 1;i <= n;i ++)if(!j || dis_a[i] > dis_a[j])j = i;return j;}
    int get_average(){int j = 0;for(int i = 1;i <= n;i ++){dis[i] = max(dis_a[i],dis_b[i]);if(!j || dis[i] < dis[j]) j = i;}return j;}
     
    int LOWER_BOUND(long long x)
    {
        int l = 0;
        int r = g.size() - 1;
        while(l < r)
        {
            int mid = l + r + 1 >> 1;
            if(g[mid].first < x) l = mid;
            else r = mid - 1;
        }
        return l;
    }
     
    void find(int x,int p,long long cnt)
    {
        g.push_back({dis[x],x});//把当前点插入 
        PLI res = {dis[x] - cnt,0}; 
    //  int fa = lower_bound(g.begin(),g.end(),res) - g.begin() - 1;
        int fa = LOWER_BOUND(dis[x] - cnt);
    //以 x 点为边界的连通块最小点的值为dis[x] - cnt,因为这棵树dis值小的在上面,所以vector里面存的是dis值递增的一个序列
    //一个是手写的,一个是STL,因为pair默认按first排序,所以我们可以用STL
        f[g[fa].second] --;
        f[x] = 1;
    //这里用了差分的思想 
        for(int i = head[x];i;i = nxt[i])
        {
            int y = to[i];
            if(y == p) continue;
            find(y,x,cnt);
            f[x] += f[y];
        }
        ans = max(ans,f[x]);
        g.pop_back();
    }
     
    int main()
    {
        cin >> n;
        for(int i = 1;i <= n - 1;i ++)
        {
            int x,y,z;
            cin >> x >> y >> z;
            add(x,y,z);//加边 
            add(y,x,z);
        }
        int u,v;
        //下面是求树的直径,因为要求每个点到直径两端的最远距离,
        //所以定义了两个数组 dis_a 和 dis_b 
        // dfs 中的 ok 代表现在求得是哪个数组 
        dfs(1,-1,1);
        u = get();
        dis_a[u] = 0;
        dfs(u,-1,1);
        v = get();
        dfs(v,-1,0);
         
        int root = get_average();
        //求最靠近直径中点的点,顺便处理出每个点的最远距离 
         
        cin >> q;
        g.push_back({-INF,0});//要有边界
        for(int i = 1;i <= q;i ++)
        {
            long long x;
            cin >> x;
            ans = 0;
            find(root,-1,x);
            cout << ans << endl;
        }
        return 0;
    }
    

    from there

  • 相关阅读:
    Java工作流引擎全局变量的介绍
    Java工作流系统-CCBPM如何自动升级?
    驰骋工作流系统-Java共工作流引擎配置定时任务
    那些年下过的大雨
    移动端APP列表点透事件处理方法
    关于React中状态保存的研究
    几个关于js数组方法reduce的经典片段
    关于React组件之间如何优雅地传值的探讨
    模拟制作网易云音乐(AudioContext)
    移动端效果之IndexList
  • 原文地址:https://www.cnblogs.com/xy0313/p/14072853.html
Copyright © 2011-2022 走看看