Codeforces Round #292 D.Drazil and Morning Exercise

题意:

有一棵带边权的树,共有 n 个节点。有一些蚯蚓需要安置在此树上,每个节点只能安放1只蚯蚓,且所有蚯蚓在树上所占据的节点必须组成一个联通块。在确定位置之后,蚯蚓会同时开始朝离自己最远的节点前进,所有蚯蚓速度均为 1
给出q次询问,每次输入一个数 l ,要求输出可能安放的最多蚯蚓数,使得最先抵达终点的蚯蚓与最后的蚯蚓抵达时间之差不超过l

数据范围:

2n105,106,q50,l1e12

样例:

12
5 9 3
2 1 7
11 7 2
6 5 5
2 5 3
6 7 2
1 4 4
8 5 7
1 3 8
11 12 3
10 8 2
10
13 14 14 13 13 4 6 7 2 1

10
10
10
10
10
3
3
5
2
1

题解:

联通块这个条件较为奇怪。对于每一个节点,我们假设它是答案联通块中深度最低之节点,然后只考虑它子树中的节点能否加入此块内,即可简化问题。
我们可以预处理出每个节点与距它最远节点之距离。在第一次dfs时,记录如下信息:

  1. 每个节点子树中距离它最远之节点。
  2. 每个节点子树中距离它次远之节点。

在第二次dfs时,我们需要额外传递一个参数 v ,它代表现在访问之节点的父节点向下且不经过该节点可走的最远距离。每次用max(,v)计算一个节点的最远距离。向下dfs时分子树最远距离经过节点和非经过节点对 v 进行修改。

现在我们把最远距离最小的节点作为根,重新调整树的父子顺序。然后我们将所有节点按最远距离从大到小排序。每次处理询问时,从前到后遍历排序后的节点序列,使用并查集将它们的子节点合并上去,并在并查集内记录已合并节点个数。最后,使用一个单调前进的指针减掉所有最远距离比该节点最远距离+l还要大的节点的答案。对每个节点取最大值就是结果。

复杂度 O(nlogn+na(n)).

代码:
#include <bits/stdc++.h>
#define maxn 100010
#define ms(x,y) memset(x,y,sizeof x)
#define inf 0x3f3f3f3f
#define fir first
#define sec second
#define lb(x) (x&(-x))
const int mod=1e9+7;
using namespace std;
typedef long long ll;
typedef pair<ll,int> pi;
int fa[maxn],v[maxn],sz[maxn];
int find(int x){
    return x==fa[x]?x:fa[x]=find(fa[x]);
} 
void merge(int x,int y){
    x=find(x);y=find(y);
    if(x==y)return;
    if(sz[x]<sz[y]){
        fa[x]=y;
        sz[y]+=sz[x];
        v[y]+=v[x];
    }else{
        fa[y]=x;
        sz[x]+=sz[y];
        v[x]+=v[y];
    }
}
int n,m;ll l;
struct edge{
    int to,dis;
};
vector<edge>graph[maxn];
void addedge(int from,int to,int dis){
    graph[from].push_back((edge){to,dis});
}
int q,pa[maxn];ll dp[maxn][2],mx[maxn];
void init(){
    for(int i=1;i<=n;i++)fa[i]=i,v[i]=sz[i]=1;
}
void dfs1(int p,int f){
//  pa[p]=f;
    for(int i=0;i<graph[p].size();i++){
        edge e=graph[p][i];if(e.to==f)continue;
        dfs1(e.to,p);
        if(dp[e.to][1]+e.dis>dp[p][1]){
            dp[p][0]=dp[p][1];
            dp[p][1]=e.dis+dp[e.to][1];
        }else if(dp[e.to][1]+e.dis>dp[p][0]){
            dp[p][0]=e.dis+dp[e.to][1];
        }
    }
}
void dfs2(int p,int f,ll v){
    mx[p]=max(dp[p][1],v);//cout<<p<<' '<<mx[p]<<endl;
    for(int i=0;i<graph[p].size();i++){
        edge e=graph[p][i];if(e.to==f)continue;
        if(dp[p][1]==dp[e.to][1]+e.dis){
            dfs2(e.to,p,max(v,dp[p][0])+e.dis);
        }else{
            dfs2(e.to,p,max(v,dp[p][1])+e.dis);
        }
    }
}
void dfs(int p,int f){
    pa[p]=f;
    for(int i=0;i<graph[p].size();i++){
        edge e=graph[p][i];if(e.to==f)continue;
        dfs(e.to,p);
    }
}
pi d[maxn];
int main(){
    scanf("%d",&n);
    for(int i=1,u,v,w;i<n;i++){
        scanf("%d%d%d",&u,&v,&w);
        addedge(u,v,w);addedge(v,u,w);
    }
    dfs1(1,0);dfs2(1,0,0);
    for(int i=1;i<=n;i++)d[i]=make_pair(mx[i],i);
    sort(d+1,d+n+1);dfs(d[1].sec,0);
    scanf("%d",&q);
    while(q--){
        scanf("%lld",&l);
        init();
        int now=n,res=0;
        for(int i=n;i;i--){
            int p=d[i].sec;
            for(int j=0;j<graph[p].size();j++){
                if(graph[p][j].to==pa[p])continue;
                merge(p,graph[p][j].to);
            }
            while(d[now].fir-d[i].fir>l)v[find(d[now].sec)]--,now--;
//          cout<<p<<' '<<v[find(p)]<<endl;
            res=max(res,v[find(p)]);
        }
        printf("%d\n",res);
    } 
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值