Luogu P5563 [Celeste-B] No More Running

空気力学の詩發表於2024-08-07

Celeste,啟動!

稍作思考就會發現這題其實很簡單,樹上路徑一眼考慮點分治

對於分治中心,很容易預先求出所有未處理的點到它的距離(模意義下),可以用這些資訊來更新中心的答案

考慮剩下的某個未處理的點 \(x\),它的答案可能由 \(x\) 到分治中心的距離 \(dis_x\),拼上分治中心到另一個點 \(y\) 的距離 \(dis_y\) 構成

由於 \(dis_x\) 是已知的定值,且 \(dis_x,dis_y\in[0,mod)\),因此不難發現只要根據它們的和要取模/不取模兩種情況討論即可:

  • \(dis_x+dis_y\) 不取模,則找到最大的 \(dis_y\) 滿足 \(dis_y\le mod-1-dis_x\),用 \(dis_x+dis_y\) 更新答案
  • \(dis_x+dis_y\) 取模,則直接找到最大的 \(dis_y\),用 \(dis_x+dis_y\)\(mod\) 取模的值更新答案

multiset 維護一下,同時注意 \(dis_x\)\(dis_y\) 不能來自同一子樹這一老生常談的問題即可

總複雜度 \(O(n\log^2 n)\)

#include<cstdio>
#include<iostream>
#include<set>
#include<vector>
#define RI register int
#define CI const int&
using namespace std;
typedef pair <int,int> pi;
const int N=100005,INF=1e9;
int n,mod,x,y,z,ans[N],dis[N]; vector <pi> v[N]; multiset <int> s;
namespace Point_Divide
{
    int rt,ots,sz[N],mx[N]; bool vis[N];
    inline void getrt(CI now=1,CI fa=0)
    {
        sz[now]=1; mx[now]=0;
        for (auto [to,w]:v[now]) if (to!=fa&&!vis[to])
        getrt(to,now),sz[now]+=sz[to],mx[now]=max(mx[now],sz[to]);
        if (mx[now]=max(mx[now],ots-sz[now]),mx[now]<mx[rt]) rt=now;
    }
    inline void insert(CI now,CI fa=0)
    {
        s.insert(dis[now]); for (auto [to,w]:v[now])
        if (to!=fa&&!vis[to]) dis[to]=(dis[now]+w)&mod,insert(to,now);
    }
    inline void remove(CI now,CI fa=0)
    {
        s.erase(s.find(dis[now])); for (auto [to,w]:v[now])
        if (to!=fa&&!vis[to]) dis[to]=(dis[now]+w)&mod,remove(to,now);
    }
    inline void calc(CI now,CI fa=0)
    {
        auto it=s.upper_bound(mod-dis[now]);
        if (it!=s.begin()) ans[now]=max(ans[now],dis[now]+*(--it));
        if (!s.empty()) ans[now]=max(ans[now],(dis[now]+*s.rbegin())&mod);
        for (auto [to,w]:v[now]) if (to!=fa&&!vis[to]) calc(to,now);
    }
    inline void solve(CI now)
    {
        vis[now]=1; dis[now]=0; insert(now);
        ans[now]=max(ans[now],*s.rbegin());
        for (auto [to,w]:v[now]) if (!vis[to])
        remove(to),calc(to),insert(to); s.clear();
        for (auto [to,w]:v[now]) if (!vis[to])
        mx[rt=0]=INF,ots=sz[to],getrt(to,now),solve(rt);
    }
    inline void divide(CI n)
    {
        mx[rt=0]=INF; ots=n; getrt(); solve(rt);
    }
};
using namespace Point_Divide;
int main()
{
    scanf("%d%d",&n,&mod); --mod;
    for (RI i=1;i<n;++i) scanf("%d%d%d",&x,&y,&z),v[x].push_back(pi(y,z)),v[y].push_back(pi(x,z));
    divide(n); for (RI i=1;i<=n;++i) printf("%d\n",ans[i]);
    return 0;
}

相關文章