2093:【22CSPS提高组】数据传输(transmit)
时间限制: 3000 ms 内存限制: 524288 KB
提交数: 172 通过数: 72
【题目描述】
小 C 正在设计计算机网络中的路由系统。
测试用的网络总共有 nn 台主机,依次编号为 11 ∼ nn。这 nn 台主机之间由 n−1n−1 根网线连接,第 ii 条网线连接个主机 aiai 和 bibi。保证任意两台主机可以通过有限根网线直接或者间接地相连。受制于信息发送的功率,主机 aa 能够直接将信息传输给主机 bb 当且仅当两个主机在可以通过不超过 kk 根网线直接或者间接的相连。
在计算机网络中,数据的传输往往需要通过若干次转发。假定小 C 需要将数据从主机 aa 传输到主机 b(a≠b)b(a≠b),则其会选择出若干台用于传输的主机 c1c1 = aa, c2c2, · · · , cm−1cm−1, cmcm = bb,并按照如下规则转发:对于所有的 1≤i<m1≤i<m, 主机 cici 将信息直接发送给 ci+1ci+1。
每台主机处理信息都需要一定的时间,第 ii 台主机处理信息需要 vivi 单位的时间。数据在网络中的传输非常迅速,因此传输的时间可以忽略不计。据此,上述传输过程花费的时间为 ∑mi=1vci∑i=1mvci。
现在总共有 qq 次数据发送请求,第 ii 次请求会从主机 sisi 发送数据到主机 titi。小 C想要知道,对于每一次请求至少需要花费多少单位时间才能完成传输。
【输入】
输入的第一行包含三个正整数 nn, QQ, kk,分别表示网络主机个数,请求个数,传输参数。数据保证 1≤n≤2×1051≤n≤2×105, 1≤Q≤2×1051≤Q≤2×105, 1≤k≤31≤k≤3。
输入的第二行包含 nn 个正整数,第 ii 个正整数表示 vivi,保证 1≤vi≤1091≤vi≤109。
接下来 n−1n−1 行,第 ii 行包含两个正整数 aiai, bibi,表示一条连接主机 aiai, bibi 的网线。保证 1≤ai,bi≤n1≤ai,bi≤n。
接下来 QQ 行,第 ii 行包含两个正整数 sisi, titi,表示一次从主机 sisi 发送数据到主机 titi的请求。保证 1≤si1≤si, ti≤nti≤n, si≠tisi≠ti。
【输出】
QQ 行,每行一个正整数,表示第 ii 次请求在传输的时候至少需要花费多少单位的时间。
【输入样例】
7 3 3
1 2 3 4 5 6 7
1 2
1 3
2 4
2 5
3 6
3 7
4 7
5 6
1 2
【输出样例】
12
12
3
【提示】
【样例 1 解释】
对于第一组请求,由于主机 44, 77 之间需要至少 44 根网线才能连接,因此数据无法在两台主机之间直接传输,其至少需要一次转发;我们让其在主机 11 进行一次转发,不难发现主机 11 和主机 44, 77 之间都只需要两根网线即可连接,且主机 11 的数据处理时间仅为 11,为所有主机中最小,因此最少传输的时间为 4+1+7=124+1+7=12。
对于第三组请求,由于主机 11, 22 之间只需要 11 根网线就能连接,因此数据直接传输就是最优解,最少传输的时间为 1+2=31+2=3。
【数据范围】
对于所有的测试数据,满足 1≤n≤2×1051≤n≤2×105, 1≤Q≤2×1051≤Q≤2×105, 1≤k≤31≤k≤3, 1≤ai,bi≤n1≤ai,bi≤n, 1≤si,ti≤n1≤si,ti≤n, si≠tisi≠ti。
测试点 | n | Q | k | 特殊性质 |
1 | ≤ 10 | ≤ 10 | =2 | 是 |
2 | =3 | |||
3 | ≤ 200 | ≤ 200 | =2 | |
4,5 | =3 | |||
6,7 | ≤ 2000 | ≤ 2000 | =1 | 否 |
8,9 | =2 | |||
10,11 | =3 | |||
12,13 | ≤ 2 × 105 | ≤ 2 × 105 | =1 | |
14 | ≤ 5 × 104 | ≤ 5 × 104 | =2 | 是 |
15,16 | ≤105 | ≤ 105 | ||
17,18,19 | ≤2 × 105 | ≤ 2 × 105 | 否 | |
20 | ≤5 × 104 | ≤ 5 × 104 | =3 | 是 |
21,22 | ≤105 | ≤ 105 | ||
23,24,25 | ≤2 × 105 | ≤ 2 × 105 | 否 |
特殊性质:保证 ai=i+1ai=i+1,而 bibi 则从 11, 22, . . . , ii 中等概率选取。
题解:
#include<bits/stdc++.h>
using namespace std;
namespace IO
{
bool EOFstate = 0;
template<typename T>inline void read(T& x)
{
x = 0; int f = 1; char c = getchar();
while(('0' > c || c > '9') && !EOFstate) { if(c == '-')f = -1; c = getchar(), EOFstate = c == EOF; }
while('0' <= c && c <= '9')x = (x << 3) + (x << 1) + c - '0', c = getchar();
x *= f;
}
template<typename T = int>inline T read()
{
T x;
x = 0; int f = 1; char c = getchar();
while(('0' > c || c > '9') && !EOFstate) { if(c == '-')f = -1; c = getchar(), EOFstate = c == EOF; }
while('0' <= c && c <= '9')x = (x << 3) + (x << 1) + c - '0', c = getchar();
x *= f;
return x;
}
template<typename T>inline void write(T x, char end = ' ')
{
if(x == 0)return putchar('0'), putchar(end), void();
if(x < 0)putchar('-'), x = -x;
char c[21], cnt = 0;
while(x)c[cnt++] = x % 10 + '0', x /= 10;
while(cnt)putchar(c[--cnt]); putchar(end);
}
}using namespace IO;
const int N = 2e5 + 5, K = 20, A = 3;
#define ll long long
#define getB(x) (31-__builtin_clz(x))
int n, q, lim;
struct Node
{
ll a[A][A];
inline void clear()
{
memset(a, 0x3f, sizeof(a));
a[0][0] = 0;
}
inline void ext()//贪心扩展状态
{
for(int i = 0; i < lim - 1; ++i)
for(int j = lim - 1; ~j; --j)
a[i + 1][j] = min(a[i + 1][j], a[i][j]);
for(int i = 0; i < lim; ++i)
for(int j = lim - 1; j > 0; --j)
a[i][j - 1] = min(a[i][j - 1], a[i][j]);
}
inline void init(ll v)
{
memset(a, 0x3f, sizeof(a));
a[0][lim - 1] = v;
for(int i = 1; i < lim; ++i)
a[i][i - 1] = 0;
}
};
Node up[N][K];
int fa[N][K], dep[N];
int val[N];
int head[N], to[N << 1], nxt[N << 1], tot;
inline void _add(int f, int t)
{
to[++tot] = t, nxt[tot] = head[f], head[f] = tot;
}
inline void add(int a, int b)
{
_add(a, b), _add(b, a);
}
void dfs(int p, int f)
{
dep[p] = dep[f] + 1, fa[p][0] = f;
for(int i = head[p]; i; i = nxt[i])
if(to[i] != f)
dfs(to[i], p);
}
inline void extend(int p)
{
up[p][0].init(val[p]);
if(lim == 3)
for(int i = head[p]; i; i = nxt[i])
up[p][0].a[1][1] = min(up[p][0].a[1][1], val[to[i]]*1ll);
up[p][0].ext();
}
inline void merge(Node& T, Node f, Node g)//合并两根网线
{
for(int a = 0; a < lim; ++a)
for(int b = 0; b < lim; ++b)
{
ll v = 0x3f3f3f3f3f3f3f3f;
for(int j = 0; j < lim; ++j)
v = min(v, f.a[a][j] + g.a[j][b]);
T.a[a][b] = v;
}
}
inline void init()
{
int mx = getB(n);
for(int k = 1, t; k <= mx; ++k)
for(int i = 1; i <= n; ++i)
{
t = fa[i][k - 1];
fa[i][k] = fa[t][k - 1];
merge(up[i][k], up[i][k - 1], up[t][k - 1]);
}
}
Node vala, valb;
inline int LCA(int a, int b)
{
vala.clear(), valb.clear();
if(dep[a] < dep[b])
swap(a, b), swap(vala, valb);
for(int i = dep[a] - dep[b], k = i & -i; i; i ^= k, k = i & -i)//没什么用的卡常(莫非是getB常数太大)
merge(vala, vala, up[a][getB(k)]), a = fa[a][getB(k)];
if(a == b)
return a;
for(int i = getB(dep[a]); ~i; --i)//有点用的卡常
if(fa[a][i] != fa[b][i])
merge(vala, vala, up[a][i]), merge(valb, valb, up[b][i]), a = fa[a][i], b = fa[b][i];
merge(vala, vala, up[a][0]), merge(valb, valb, up[b][0]);
return fa[a][0];
}
signed main()
{
read(n), read(q), read(lim);
for(int i = 1; i <= n; ++i)
read(val[i]);
for(int i = 2; i <= n; ++i)
add(read(), read());
dfs(1, 0);
for(int i = 1; i <= n; ++i)
extend(i);
init();
for(int i = 1, s, t; i <= q; ++i)
{
read(s), read(t);
int lca = LCA(s, t);
merge(vala, vala, up[lca][0]);//将lca也合入一根网线
ll v = LLONG_MAX;
for(int i = 0; i < lim; ++i)
v = min(v, vala.a[0][i] + valb.a[0][lim - i - 1]);
write(v, '\n');
}
return 0;
}