CF1654-G. Snowy Mountain(2900) GOOD

在这里插入图片描述在这里插入图片描述

思路

分析最优解性质
  • 任意两个点高度至多相差1
  • 任取一条最优滑雪路径。最后反复出现的点一定最多仅有2个。其余的路径上的点互不相同
    • 若不然,可以将其余的重复点构成的路径加到这两个点的反复中。
  • 如果能反复,考虑在点 u u u开始进行反复。开始点为点v。则最长能滑动的总路径为 2 h v − h u 2h_v - h_u 2hvhu这是因为从点v到点u多余能量 h v − h u h_v - h_u hvhu,在u点开始与其邻居进行反复,直到能量消耗为0.至此,共消耗能量 2 ( h v − h u ) 2 (h _v - h_u) 2(hvhu)。加上从u点到doge能滑动最长的距离 h u h_u hu
  • 由上式可见,若 h u h_u hu较小,则滑动的距离较大。因此对每个结点找其能到达的高度最低的u点,如果能找到答案为上述式子,否则答案为自身的高度

逆向考察

  • 对上述u点,其必须满足至少有一个邻居与其有同样的高度。对于这样的点对 u , v u,v u,v,v是u的与其相同高度的邻居。其到达它们最近的doge的路径互不相交。因此 ∑ u ∈ S h u \sum_{u \in S} h_u uShu较小。可以证明其是 O ( n ) O(n) O(n)级别的。于是不同的 h u h_u hu个数是 n \sqrt{n} n 级别的。
  • 对每种可能的高度对应的结点考察其能够到达的所有结点。

实现(官方)

  • 先使用多源bfs求出所有点的高度。
while(!q.empty()){
    		int x=q.front();q.pop();
    		for(auto c:adj[x]){
    			if(d[c] == -1){
    				d[c]=d[x]+1;
    				q.push(c);
    			}
    		}
    	}
  • 利用h数组记录可行的高度i是几号高度,利用g数组记录z号高度的实际高度。
    	for(int i=1; i<=n ;i++){
    		f[d[i]].push_back(i);
    		for(auto c:adj[i]){
    			if(d[c]==d[i] && c>i){
    				use[d[i]]=true;
    			}
    		}
    	}
    	for(int i=0; i<=n ;i++){
    		if(use[i]){
    			g[++z]=i;
    			h[i]=z;
    		}
    	}/*
  • ans[i][z]表示i号结点到达z号高度满足邻居有相同z号高度的结点所需要的最少初始能量,其中z的取值是 n \sqrt{n} n 级别的。
  • 将结点按高度进行分层。假设下一层所需要到达的每一种可能高度的最低能量已经求出,下一层对上一层按照公式:
    a n s [ x ] [ i ] = m i n ( a n s [ x ] [ i ] , m a x ( 0 , a n s [ c ] [ i ] − 1 ) ) ans[x][i]=min(ans[x][i],max(0,ans[c][i]-1)) ans[x][i]=min(ans[x][i],max(0,ans[c][i]1))
    其中c是下一层结点,x是当前层结点。
    			for(auto c:adj[x]){
    				if(d[c]==d[x]-1){
    					for(int i=1; i<=z ;i++) ans[x][i]=min(ans[x][i],max((ll)0,ans[c][i]-1));
    				}
    			}
  • 同层结点的转移,利用up and down dp,通过两次dfs(待学习)
    a n s [ c ] [ i ] = m i n ( a n s [ c ] [ i ] , a n s [ i d ] [ i ] + 1 ) ans[c][i]=min(ans[c][i],ans[id][i]+1) ans[c][i]=min(ans[c][i],ans[id][i]+1)
    其中id是c的邻居,对每种高度i循环执行
void dfs(int id,int p){
    	vis[id]=true;
    	for(auto c:adj[id]){
    		if(c==p || d[c]!=d[id]) continue;
    		dfs(c,id);
    		for(int i=1; i<=z ;i++){
    			ans[id][i]=min(ans[id][i],ans[c][i]+1);
    		}
    		ans[id][h[d[id]]]=0;
    	}
    }
    void dfs2(int id,int p){
    	for(auto c:adj[id]){
    		if(c==p || d[c]!=d[id]) continue;
    		for(int i=1; i<=z ;i++){
    			ans[c][i]=min(ans[c][i],ans[id][i]+1);
    		}
    		ans[c][h[d[c]]]=0;
    		dfs2(c,id);
    	}
    }
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define fi first
    #define se second
    #define int long long
    const ll mod=998244353;
    const int N=2e5+1;
    int n;
    int d[N];
    queue<int>q;
     
    vector<int>adj[N];
    int g[N],h[N],z;
    bool use[N];
    vector<int>ans[N];
     
    vector<int>f[N];
    bool vis[N];
    void dfs(int id,int p){
    	vis[id]=true;
    	for(auto c:adj[id]){
    		if(c==p || d[c]!=d[id]) continue;
    		dfs(c,id);
    		for(int i=1; i<=z ;i++){
    			ans[id][i]=min(ans[id][i],ans[c][i]+1);
    		}
    		ans[id][h[d[id]]]=0;
    	}
    }
    void dfs2(int id,int p){
    	for(auto c:adj[id]){
    		if(c==p || d[c]!=d[id]) continue;
    		for(int i=1; i<=z ;i++){
    			ans[c][i]=min(ans[c][i],ans[id][i]+1);
    		}
    		ans[c][h[d[c]]]=0;
    		dfs2(c,id);
    	}
    }
    main(){
    	ios::sync_with_stdio(false);cin.tie(0);
    	cin >> n;
    	for(int i=1; i<=n ;i++){
    		int x;cin >> x;
    		if(x==1) {q.push(i);d[i] = 0;}
    		else d[i]=-1;		
    	}
    	for(int i=1; i<n ;i++){
    		int u,v;cin >> u >> v;
    		adj[u].push_back(v);
    		adj[v].push_back(u);
    	}
    	while(!q.empty()){
    		int x=q.front();q.pop();
    		for(auto c:adj[x]){
    			if(d[c] == -1){
    				d[c]=d[x]+1;
    				q.push(c);
    			}
    		}
    	}
    	for(int i=1; i<=n ;i++){
    		f[d[i]].push_back(i);
    		for(auto c:adj[i]){
    			if(d[c]==d[i] && c>i){
    				use[d[i]]=true;
    			}
    		}
    	}
    	for(int i=0; i<=n ;i++){
    		if(use[i]){
    			g[++z]=i;
    			h[i]=z;
    		}
    	}/*
    	for(int i=1; i<=n ;i++){
    		cout << d[i] << ' ';
    	}
    	cout << endl;*/
    	for(int i=1; i<=n ;i++){
    		ans[i].resize(z+1);
    	}
    	for(int j=0; j<=n ;j++){
    		for(auto x:f[j]){
    			for(int i=1; i<=z ;i++) ans[x][i]=1e9;
    			for(auto c:adj[x]){
    				if(d[c]==d[x]-1){
    					for(int i=1; i<=z ;i++) ans[x][i]=min(ans[x][i],max((ll)0,ans[c][i]-1));
    				}
    			}
    		}
    		
    		for(auto x:f[j]){
    			if(!vis[x]){
    				dfs(x,0);
    				dfs2(x,0);
    			}
    		}
    		/*for(auto x:f[j]){
    			cout << x << ": ";
    			for(int i=1; i<=z ;i++) cout << ans[x][i] << ' ';
    			cout << '\n';
    		}*/
    	}
    	for(int i=1; i<=n ;i++){
    		int res=d[i];
    		for(int j=1; j<=z ;j++){
    			if(ans[i][j]==0) res=max(res,d[i]*2-g[j]);
    		}
    		cout << res << ' ';
    	}
    	cout << '\n';
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值