🎊 NOIP-EMU-141031-treas 藏宝图

NOIP-EMU-141031-treas 藏宝图

藏宝图 (treas)

背景

Czy 爬上黑红树, 到达了一个奇怪的地方……

题目描述

Czy 发现了一张奇怪的藏宝图. 图上有 n 个点, m 条无向边. 已经标出了图中两两之间距离 dist. 但是 czy 知道, 只有当图刚好又是一颗树的时候, 这张藏宝图才是真的. 如果藏宝图是真的, 那么经过点 x 的边的边权平均数最大的那个 x 是藏着宝物的地方. 请计算这是不是真 的藏宝图, 如果是真的藏宝之处在哪里.

格式

输入数据第一行一个数 T, 表示 T 组数据.

对于每组数据, 第一行一个 n, 表示藏宝图上的点的个数.

接下来 n 行, 每行 n 个数, 表示两两节点之间的距离.

输出一行或两行. 第一行 “Yes” 或 “No”, 表示这是不是真的藏宝图.

若是真的藏宝图, 第二行再输出一个数, 表示哪个点是藏宝之处.

样例输入

2

3

0 7 9

7 0 2

9 2 0

3

0 2 7

2 0 9

7 9 0

样例输出

Yes

1

Yes

3

样例解释

第一棵树的形状是 1--2--3. 1、2 之间的边权是 7, 2、3 之间是 2.

第二棵树的形状是 2--1--3. 2、1 之间的边权是 2, 1、3 之间是 7.

数据范围

对于 30% 数据,\(n \leq 50\),\(1 \leq 树上的边的长度 \leq 10^9\).

对于 50% 数据,\(n \leq 600\).

对于 100% 数据,\(1 \leq n \leq 2500\), 除 30% 小数据外任意 \(0 \leq dist[i][j] \leq 10^9\),\(T \leq 5\)

Explanation

此题看上去很玄学的样子。

先生成一个最小生成树, 我们可以证明: 若原图是一棵树, 则其最小生成树必定是其原图. 得到这个结论以后, 我们将原来的完全图 (当然是一个稠密图, 极其稠密的稠密图) 进行一下 生成最小生成树.

显然我们不能用 Kruskal, 因为在稠密图上被 Prim 秒杀; 另外不能用堆优化 Prim, 否则它会被优化到 \(O(n^2 \cdot log(n))\); 所以最后只能用 Prim.

然后有一段很玄学的 \(O(n^2 \cdot log(n))\) 最短路代码写在底下.

最后交题的时候由于是在本地评测, 所以就闹出了一段笑话:

在隔壁的台式机上由于出题人 恶意卡常 (并且出题人强行立 flag 说不用开 long long 但是实际是需要的), 只能跑到 3s 左右. 在我的笔记本上大概 std 需要跑 31s, 然后 就只能强行调时限了.

我的笔记本

隔壁的台式机

Source Code

#include

#include

#include

#include

#include

using namespace std;

typedef long long lli;

const int maxn = 2600;

const lli infinit = 0x007f7f7f7f7f7f7fll;

class MinimumSpanTree

{

public:

struct edge {

int u, v; lli len;

edge *next;

} *edges[maxn], epool[2 * maxn];

int n, root, ecnt;

void addedge(int u, int v, lli len)

{

edge *p = &epool[++ecnt],

*q = &epool[++ecnt];

p->u = u; p->v = v; p->len = len;

p->next = edges[u]; edges[u] = p;

q->u = v; q->v = u; q->len = len;

q->next = edges[v]; edges[v] = q;

return ;

}

lli dist[maxn][maxn];

lli dis[maxn]; // The distance used in eval() function.

int from[maxn]; // The "from" as in SPFA, relaxed nodes

bool visited[maxn]; // Whether visited or not

void eval(void)

{

// Resetting initial settings

for (int i = 1; i <= n; i++)

visited[i] = false, dis[i] = infinit;

dis[1] = 0;

// Iterate exactly n times with i.

for (int i = 1; i <= n; i++) {

int p = -1;

// Select previously nearest node

for (int j = 1; j <= n; j++)

if (!visited[j])

if (p <= 0 || dis[j] < dis[p])

p = j; // Assign better node

if (p < 0)

break; // Search failed

visited[p] = true;

addedge(from[p], p, dist[from[p]][p]);

// Relaxing edges

for (int j = 1; j <= n; j++)

if (!visited[j] && dist[p][j] < dis[j]) {

dis[j] = dist[p][j];

from[j] = p;

}

continue;

}

return ;

}

void clear(void)

{

memset(edges, 0, sizeof(edges));

memset(epool, 0, sizeof(epool));

memset(dist, 0, sizeof(dist));

memset(from, 0, sizeof(from));

n = 0, ecnt = 0;

return ;

}

} graph;

class ShortestPath

{

public:

struct edge {

int u, v; lli len;

edge *next;

} *edges[maxn], epool[2 * maxn];

int n, ecnt;

void addedge(int u, int v, lli len)

{

edge *p = &epool[++ecnt];

p->u = u; p->v = v; p->len = len;

p->next = edges[u]; edges[u] = p;

return ;

}

bool visited[maxn];

lli dist[maxn][maxn];

lli edge_sum[maxn];

int edge_cnt[maxn];

vector vec; // Vector optimizing shortest path calculation

void dfs(int p)

{

for (edge *ep = edges[p]; ep; ep = ep->next) {

// Already visited

if (visited[ep->v])

continue;

for (unsigned int j = 0; j < vec.size(); j++) {

dist[ep->v][vec[j]] = dist[vec[j]][ep->v] = dist[vec[j]][p] + ep->len;

edge_sum[p] += ep->len;

edge_sum[ep->v] += ep->len;

edge_cnt[p]++;

edge_cnt[ep->v]++;

}

vec.push_back(ep->v);

visited[ep->v] = true;

dfs(ep->v);

}

return ;

}

void eval(void) {

dfs(1); }

void clear(void)

{

memset(edges, 0, sizeof(edges));

memset(epool, 0, sizeof(epool));

memset(visited, 0, sizeof(visited));

memset(dist, 0, sizeof(dist));

memset(edge_sum, 0, sizeof(edge_sum));

memset(edge_cnt, 0, sizeof(edge_cnt));

n = 0, ecnt = 0;

vec.clear();

return ;

}

bool cp_vis[maxn];

void cp_dfs(int p)

{

cp_vis[p] = true;

for (MinimumSpanTree::edge *ep = graph.edges[p]; ep; ep = ep->next)

if (!cp_vis[ep->v]) {

addedge(p, ep->v, ep->len);

cp_dfs(ep->v);

}

return ;

}

void copy_edges(void)

{

memset(cp_vis, 0, sizeof(cp_vis));

cp_dfs(1);

}

} spfa;

int T, n, m;

int select_best(void)

{

int best_val = 0,

best_pos = 1;

for (int i = 1; i <= n; i++) {

if (spfa.edge_cnt[i] <= 0)

continue;

int tmp = spfa.edge_sum[i] / spfa.edge_cnt[i];

if (tmp > best_val) {

best_val = tmp;

best_pos = i;

}

}

return best_pos;

}

int main(int argc, char** argv)

{

scanf("%d", &T);

for (int idx = 1; idx <= T; idx++) {

scanf("%d", &n);

graph.clear();

spfa.clear();

graph.n = spfa.n = n;

for (int i = 1; i <= n; i++)

for (int j = 1; j <= n; j++)

scanf("%lld", &graph.dist[i][j]);

// Evaluating MST, complexity O(n^2)

graph.eval();

// Copying edges data from MST to shortest path

spfa.copy_edges();

// Evaluating SPFA, complexity O(n^2 * logn)

spfa.eval();

// Judging

bool okay = true;

for (int i = 1; i <= n && okay; i++)

for (int j = 1; j <= n && okay; j++)

if (spfa.dist[i][j] > graph.dist[i][j])

okay = false;

printf("%s\n", okay ? "Yes" : "No");

// Print the "Best Place for Treasure Hiding"

if (okay)

printf("%d\n", select_best());

}

return 0;

}

🎁 相关推荐

索立信A200
🎯 365足彩推荐

索立信A200

📅 09-13 👀 9105
在微信要红包犯法吗(微信红包的法律风险分析)
🎯 365足彩推荐

在微信要红包犯法吗(微信红包的法律风险分析)

📅 08-14 👀 4336
打印机驱动怎么下载 详细下载安装教程
🎯 bat365官方网站

打印机驱动怎么下载 详细下载安装教程

📅 08-24 👀 965