权限门
题目大意:给一棵树,有N个点,有边权。所有无向路径中,请你输出前M大的。
N<=50000
M<=Min(300000,n*(n-1) /2)
树上超级钢琴。(拖延症的结果就是现在才写这题)
学了一种点分治的套路,在做点分治的时候求出整棵树的dfs序。可以发现,每个点在dfs序列中出现不超过log次。我们不妨称其为点分治序。
然后类比超级钢琴,将序列转换到树上。强制路径过分治中心,然后每一条路径的长度对应Dis[x]+Dis[y]。其中x和y要求在分治中心的不同子树内。这里我们不可以直接做然后减什么的,有一种方便的技巧就是枚举儿子,每个儿子dfs一次。记录每个点为路径尾能取到的头的区间[L,R]。
接下来,直接在点分治序上做ST表,丢进大根堆里然后分裂区间即可,这里跟在序列上几乎一样。
时间复杂度是两个log的。
还有一种二分的做法,加上每次点分治总共有3个log,是过不了的,但是先点分治一次,排好序并记下来,然后就只有两个log了,也是很强的。
#include
#define maxn 800010
#define Lg 20
using namespace std;
int n, m, cur = -1;
struct List{
List *next;
int obj, len;
}*head[maxn], Edg[maxn<<1];
void Addedge(int a, int b, int c){
Edg[++cur].next = head[a];
Edg[cur].obj = b;
Edg[cur].len = c;
head[a] = Edg+cur;
}
bool Vis[maxn];
int Root, Sum, Dfn;
int son[maxn], siz[maxn], fa[maxn];
int T[maxn], f[Lg][maxn], g[Lg][maxn];
int tL[maxn], tR[maxn], Dis[maxn];
struct World{
int x, y, l, r;
World() {}
World(int _x, int _y, int _l, int _r){
x = _x; y = _y; l = _l; r = _r;
}
bool operator < (const World& OTHER) const{
return Dis[x] + Dis[y] < Dis[OTHER.x] + Dis[OTHER.y];
}
};
priority_queue Q;
void GetRoot(int x, int ff){
siz[x] = 1; son[x] = 0;
for(List *p = head[x]; p; p = p->next){
int v = p->obj;
if(v == ff || Vis[v]) continue;
GetRoot(v, x);
siz[x] += siz[v];
son[x] = max(son[x], siz[v]);
fa[v] = x;
}
son[x] = max(son[x], Sum-siz[x]);
if(son[x] < son[Root]) Root = x;
}
void Dfs(int x, int ff, int L, int R, int dep){
tL[++Dfn] = L; tR[Dfn] = R; Dis[Dfn] = dep;
for(List *p = head[x]; p; p = p->next){
int v = p->obj, l = p->len;
if(v == ff || Vis[v]) continue;
Dfs(v, x, L, R, dep+l);
}
}
void Solve(int x){
Vis[x] = true;
siz[fa[x]] = Sum - siz[x];
Dfn ++;
tL[Dfn] = Dfn; tR[Dfn] = Dfn; Dis[Dfn] = 0;
int res = Dfn;
for(List *p = head[x]; p; p = p->next){
int v = p->obj, l = p->len;
if(Vis[v]) continue;
Dfs(v, 0, res, Dfn, l);
}
for(List *p = head[x]; p; p = p->next){
int v = p->obj;
if(Vis[v]) continue;
Root = 0;
Sum = siz[v];
GetRoot(v, 0);
Solve(Root);
}
}
void Pre(){
T[1] = 0;
for(int i = 2; i <= Dfn; i++) T[i] = T[i>>1] + 1;
for(int i = 1; i <= Dfn; i++) f[0][i] = Dis[i], g[0][i] = i;
for(int i = 1; i <= T[Dfn]; i++)
for(int j = 1; j <= Dfn-(1<1; j++){
if(f[i-1][j] > f[i-1][j+(1<<(i-1))]){
f[i][j] = f[i-1][j];
g[i][j] = g[i-1][j];
}
else{
f[i][j] = f[i-1][j+(1<<(i-1))];
g[i][j] = g[i-1][j+(1<<(i-1))];
}
}
}
void Push(int x, int l, int r){
if(l > r) return;
int t = T[r-l+1], pos;
if(f[t][l] > f[t][r-(1<1]) pos = g[t][l];
else pos = g[t][r-(1<1];
Q.push(World(x, pos, l, r));
}
int main(){
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) head[i] = NULL;
int a, b, c;
for(int i = 1; i < n; i++){
scanf("%d%d%d", &a, &b, &c);
Addedge(a, b, c);
Addedge(b, a, c);
}
Root = 0;
son[0] = Sum = n;
GetRoot(1, 0);
Solve(Root);
Pre();
for(int i = 1; i <= Dfn; i++) Push(i, tL[i], tR[i]);
for(int i = 1; i <= m; i++){
World tmp = Q.top(); Q.pop();
printf("%d\n", Dis[tmp.x] + Dis[tmp.y]);
Push(tmp.x, tmp.l, tmp.y-1);
Push(tmp.x, tmp.y+1, tmp.r);
}
return 0;
}