Codeforces Round #292 D.Drazil and Morning Exercise

CDQZOIERS發表於2017-10-21

題意:

有一棵帶邊權的樹,共有n

n
個節點。有一些蚯蚓需要安置在此樹上,每個節點只能安放1
1
只蚯蚓,且所有蚯蚓在樹上所佔據的節點必須組成一個聯通塊。在確定位置之後,蚯蚓會同時開始朝離自己最遠的節點前進,所有蚯蚓速度均為1
1

給出q
q
次詢問,每次輸入一個數l
l
,要求輸出可能安放的最多蚯蚓數,使得最先抵達終點的蚯蚓與最後的蚯蚓抵達時間之差不超過l
l

資料範圍:

2n105,106,q50,l1e12

2 ≤ n ≤ 10^5,邊權 ≤ 10^6,q ≤ 50,l ≤ 1e12

樣例:

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

v
,它代表現在訪問之節點的父節點向下且不經過該節點可走的最遠距離。每次用max(,v)
max(子樹中最遠距離,v)
計算一個節點的最遠距離。向下dfs時分子樹最遠距離經過節點和非經過節點對v
v
進行修改。

現在我們把最遠距離最小的節點作為根,重新調整樹的父子順序。然後我們將所有節點按最遠距離從大到小排序。每次處理詢問時,從前到後遍歷排序後的節點序列,使用並查集將它們的子節點合併上去,並在並查集內記錄已合併節點個數。最後,使用一個單調前進的指標減掉所有最遠距離比該節點最遠距離+l

+l
還要大的節點的答案。對每個節點取最大值就是結果。

複雜度O(nlogn+na(n)).

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;
}

相關文章