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