题目大意:
补充说明:这里的不相交指的是每一个特殊点属于且仅属于一个对,不是说在树上的路径不相交。
Solution
- 一道非常傻的贪心,但是我太弱了没做出来。
- 其实一句话就能讲完但是我太弱了想多说说。
- 先说说我的弱鸡思路。
- 我的思路大概是先确定一个根,记 d u d_u du 为 u u u 到根的距离。
- 那么所有点对 ( u i , v i ) (u_i,v_i) (ui,vi) 的贡献即为: max ∑ d u i + d v i − 2 d l c a ( u i , v i ) \max\sum{d_{u_i}+d_{v_i}-2d_{lca(u_i,v_i)}} max∑dui+dvi−2dlca(ui,vi)
- 也就是 ∑ d − 2 min ∑ d l c a ( u i , v i ) \sum d-2\min\sum{d_{lca(u_i,v_i)}} ∑d−2min∑dlca(ui,vi)。
- 显然选哪个根是不影响最优性的。
- 我们要做的是,最小化所有点对 l c a lca lca 的 d d d 的和。我一个开始的想法是从下往上 D P DP DP,但是时间复杂度是 O ( k 3 ) O(k^3) O(k3) 的,并且求方案也很麻烦。
- 还有一个想法就是从上往下贪心,但是想要保证正确性不好做到。
- 于是我这题就 G G GG GG 了。拿了高达 10 10 10 分,还有 10 10 10 分应该是我爆搜写丑了,T掉了。
- 考完发现这是这场的签到题。
- 我的思路已经很接近了……就是有一个地方脑残了没想清楚。
- 我们看到 ∑ d − 2 min ∑ d l c a ( u i , v i ) \sum d-2\min\sum{d_{lca(u_i,v_i)}} ∑d−2min∑dlca(ui,vi) 这个式子不免想到如果我们能钦点 ∑ d l c a ( u i , v i ) = 0 \sum{d_{lca(u_i,v_i)}}=0 ∑dlca(ui,vi)=0,那么我们就能取到一个肯定是最优的解。
- 这要求每个点对的两个点都来自根的两棵子树。
- 满足这一点的充要条件就是根最大的子树的大小不超过 k 2 \frac{k}{2} 2k,这里大小定义为子树包含的特殊点数量。
- 如果满足这个条件,我们可以每次把两个大小最大的子树拿出一对点配对。这样一定能配完。
- 然后我们发现如果选 1 1 1 为根是不一定满足这个条件的。
- 但是这个条件在什么时候能满足呢?
喵的你选重心为根不就好了 Q A Q QAQ QAQ。- 然后就做完了。
- 如果你按那个贪心策略写是 O ( n log n ) O(n\log n) O(nlogn) 的。不过我们可以线性实现。
- 具体地,我们先找到重心,然后以重心为根求出 d f s dfs dfs 序。
- 然后对于 d f s dfs dfs 序 i ≤ k 2 i\le \frac{k}{2} i≤2k 的,我们只要和 i + k 2 i+\frac{k}{2} i+2k 配对就好。
- 根据树的重心的性质, i i i 和 i + k 2 i+\frac{k}{2} i+2k 一定来自不同子树。
- 时间复杂度 O ( n ) O(n) O(n)。
- 该题如果从另一个方向想也是可以的,压根不需要推这种式子。
- 我们考虑对于一条边,它对答案的贡献次数有个上界 min ( x , y ) \min(x,y) min(x,y), x , y x,y x,y 是这条边两侧的特殊点数量。
- 通过选取重心为根,钦点每对点来自不同子树,就可以取到这个上界。
- 为啥呢,我们考虑一条边 ( u , f a u ) (u,fa_u) (u,fau),其中以重心为根 f u f_u fu 比 u u u 的深度小。
- 然后我们根据这个重心的性质,点数较少的一侧必然为 u u u 的子树。又因为 u u u 的子树里面的所有特殊点都连到外面(连到别的子树)了。因此取到了上界。
- 然后就可以证明这样做是最优的了。
#include <bits/stdc++.h>
template <class T>
inline void read(T &x)
{
static char ch;
while (!isdigit(ch = getchar()));
x = ch - '0';
while (isdigit(ch = getchar()))
x = x * 10 + ch - '0';
}
template <class T>
inline bool relax(T &x, const T &y)
{
return x < y ? x = y, true : false;
}
template <class T>
inline bool tense(T &x, const T &y)
{
return x > y ? x = y, true : false;
}
const int MaxLog = 19;
const int MaxNV = 1e5 + 5;
const int INF = 0x3f3f3f3f;
const int MaxNE = MaxNV << 1;
struct halfEdge
{
int v, w;
halfEdge *next;
};
halfEdge adj_pool[MaxNE], *adj[MaxNV], *adj_tail = adj_pool;
#define trav(u) for (halfEdge *e = adj[u]; e; e = e->next)
int n, K;
int maxsze = INF, G;
int sze[MaxNV], son[MaxNV];
bool is_key[MaxNV];
int cnt, ans[MaxNV];
inline void addEdge(int u, int v, int w)
{
adj_tail->v = v;
adj_tail->w = w;
adj_tail->next = adj[u];
adj[u] = adj_tail++;
}
inline void dfs_init(int u, int pre)
{
sze[u] = is_key[u];
son[u] = 0;
trav(u) if (e->v != pre)
{
dfs_init(e->v, u);
sze[u] += sze[e->v];
relax(son[u], sze[e->v]);
}
if (tense(maxsze, std::max(son[u], n - sze[u])))
G = u;
}
inline void dfs_push(int u, int pre)
{
if (is_key[u]) ans[++cnt] = u;
trav(u) if (e->v != pre)
dfs_push(e->v, u);
}
int main()
{
// freopen("A.in", "r", stdin);
read(n), read(K);
for (int i = 1; i < n; ++i)
{
int u, v, w;
read(u), read(v), read(w);
addEdge(u, v, w), addEdge(v, u, w);
}
for (int i = 1; i <= K; ++i)
{
int x; read(x);
is_key[x] = true;
}
dfs_init(1, 0);
dfs_push(G, 0);
K >>= 1;
for (int i = 1; i <= K; ++i)
printf("%d %d\n", ans[i], ans[i + K]);
return 0;
}