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

posted @ 2020-12-02 11:43  星&夜  阅读(62)  评论(0编辑  收藏  举报