D. Paint the Tree

https://codeforces.com/problemset/problem/1975/D
分析:
观察样例可以发现,对于PB第一次在位置 r 接触到红点之后,接下来的怎么走完全可以有PB说了算,情况不会更差。同时还能发现,大部分边都是需要走两遍的,只有最后一条路径只需要走一遍。所以只需要将距离将距离 r 最长的路径 d 放到最后即可,这部分的步数为 2*(n-1)- d。最终结果应该是:到点 r 的步数 + 2*(n-1)- d。 

现在考虑点 r 的位置,可以猜测 r 在 b 到 a 的中点附近。如果 r 在另一个位置,可以将 r 从中点移过去。此时虽然 d 的值可能增 1,但是 d 的值每增 1,PA 和 PB 相遇的时间也会至少增加 1 ,因此答案不会更优。

#include<bits/stdc++.h>
using namespace std;
/*
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
*/
// #define int long long
#define ld long double
//#define INT __int128
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<long long, long long> PLL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
const ld eps = 1e-12;
const int N = 5e5 + 10, M = N + 10;
int n;
vector<int>g[N];
int a,b;

struct DIJKSTRA {
	// vector<PII>g[M];
	int dist[M];
    int cur[N];
	bool vis[M];
	int idx = n;
    void init(int u){
        idx=u;
    }
    void dij(int u) {
		for (int i = 1; i <= idx; i++)dist[i] = INF, vis[i] = 0,cur[i]=-1;
		dist[u] = 0;
		priority_queue<PII, vector<PII>, greater<PII>>q;
		q.push({ dist[u],u });
		while (!q.empty()) {
			int t = q.top().second;
			q.pop();
			if (vis[t])continue;
			vis[t] = 1;
			for (int i = 0; i < g[t].size(); i++) {
				int j = g[t][i], w = 1;
				if (dist[j] > dist[t] + w) {
					dist[j] = dist[t] + w;
                    cur[j]=t;
					q.push({ dist[j],j });
				}
			}
		}
		// return dist[n];
	}
}st;

void solve(){
    cin>>n;
    cin>>a>>b;
    for(int i=1;i<=n;i++){
        g[i].clear();
    }
    st.init(n+5);
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        g[u].push_back(v);
        g[v].push_back(u);
    }
    st.dij(b);
    int d=st.dist[a]/2;
    int r=a;
    int ans=(st.dist[a]+1)/2+2*(n-1);
    // cout<<"_________________"<<d<<" "<<ans<<endl;
    while(d&&st.cur[r]!=-1){
        r=st.cur[r];
        d--;
    }


    st.init(n+5);
    st.dij(r);
    int mx=0;
    for(int i=1;i<=n;i++){
        mx=max(mx,st.dist[i]);
        // cout<<"++++  "<<i<<" "<<st.dist[i]<<endl;
    }
    ans-=mx;
    cout<<ans<<endl;
}

signed main(){
    // ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
    int T=1,cas=1;
    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值