Codeforces Round #270 D题:Design Tutorial: Inverse the Problem ,最短路+LCA+树上前缀和

Design Tutorial: Inverse the Problem

题意:给你一个距离矩阵,让你证明该矩阵是不是一颗加权树的距离矩阵。

对于一个点u,与它距离最近的点一定是与它直接相连的,所以对矩阵求一次最短路,可以得到这颗树。然后只需要求出树上任意两点之间的距离是否与矩阵的距离相同就可以了。

求树上任意两点距离,可以使用LCA+树上前缀和,两点间距离等于两点到公共祖先距离之和。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e3+100;
int n;
ll d[N][N];
vector<int> g[N];
int vis[N];
struct node{
	int u, fa, w;
	node(){}
	node(int u,int fa,int w):u(u),fa(fa),w(w){}
	bool operator < (const node &op) const{
		return w > op.w;
	}
};
priority_queue<node> q;
void Dijsktra(){
	q.push(node(1,0,0));
	while(!q.empty()){
		int u=q.top().u;
		int fa=q.top().fa;
		q.pop();
		if(vis[u]) continue;
		vis[u] = 1;
		g[fa].push_back(u);
		for(int i = 1; i <= n; i++){
			if(!vis[i]) q.push(node(i,u,d[u][i]));
		}
	}
}
ll sum[N] = {0};
int depth[N];
int f[N][20];
void dfs(int fa,int u){
	depth[u] = depth[fa] + 1;
	//
	sum[u] = sum[fa] + d[fa][u];
	//
	f[u][0] = fa;
	for(int i = 1; (1<<i) <= depth[u]; i++){
		f[u][i] = f[f[u][i-1]][i-1];
	}
	for(int i = 0; i < g[u].size(); i++){
		if(g[u][i] != fa)dfs(u,g[u][i]);
	}
}


int LCA(int x,int y){
	if(depth[x] < depth[y]){
		swap(x,y);
	}
	for(int i = depth[x]-depth[y],j=0; i>0 ; i>>=1,j++){
		if(i&1){
			x=f[x][j];
		}
	}
	if(x == y) return x;
	int h;
	for(h = 0; (1<<h) <= depth[x]; h++) ;//算h最大值 
	for( ; h >=0 ; h--){
		if((1<<h) <= depth[x] && f[x][h] != f[y][h]){
			x = f[x][h];
			y = f[y][h];
		}
	}
	return f[x][0];
}



int main(){
	ios::sync_with_stdio(false);
	cin >> n;
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= n; j++){
			cin >> d[i][j];
		}
	}
	for(int i = 1; i <= n; i++){
		if(f[i][i]){
			cout << "NO\n";
			return 0;
		}
		for(int j = 1; j <= n; j++){
			if(f[i][j] != f[j][i] || (d[i][j] == 0 && i != j)){
				cout << "NO\n";
				return 0;
			}
		}
	}
	Dijsktra();
	dfs(0,1);
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= n; j++){
			int dep = LCA(i,j);
			ll dd = sum[i] + sum[j] - 2*sum[dep];
			if(dd != d[i][j]){
				cout << "NO\n";
				return 0;
			}
		}
	}
	cout << "YES\n";
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值