opencup-gym102391

 ACM  树  DSU on tree  MDST 󰈭 1276字

J RQD-IGVA树 树上DSU

最开始以为险段长度跟n无关,所以加了根[1, n]的线段进去就WA到死,后来改成[1, 1000000]即可。

大概是叫dsu on tree,可以处理一些与子树有关的询问,复杂度Onlogn

可惜IGVA不知道场上什么地方写挫了 场后10分钟A了

cpp
 1const int maxn = 3e5 + 10;
 2 
 3int n, w[maxn];
 4struct star{int to, next;};
 5int head[maxn], top = 0;
 6star edge[maxn];
 7void add(int u, int v){
 8    edge[top].to = v;
 9    edge[top].next = head[u];
10    head[u] = top++;
11}
12 
13struct NODE{
14    int l, r, w;
15    bool operator < (const NODE &b) const{
16        if(l != b.l) return l < b.l;
17        else return r > b.r;
18    }
19};
20vector<NODE> vec;
21priority_queue<ll> pq[maxn];
22int conv[maxn];
23vector<ll> temp;
24 
25//合并边i和边j的优先队列,放到i里面
26void merge(int i , int j){
27    temp.clear();
28    if(pq[conv[i]].size() < pq[conv[j]].size()) swap(conv[i], conv[j]);
29 
30    while(!pq[conv[j]].empty()){
31        temp.push_back(pq[conv[i]].top() + pq[conv[j]].top());
32        pq[conv[i]].pop(); pq[conv[j]].pop();
33    }
34    for(auto x: temp) pq[conv[i]].push(x);
35}
36 
37void sove(int now){
38    for(int i = head[now]; ~i; i = edge[i].next){
39        sove(edge[i].to);
40        merge(now, edge[i].to);
41    }
42    pq[conv[now]].push(vec[now].w);
43}
44 
45stack<int> sta;
46 
47int main(){
48    // Fastin;
49    clr(head, -1);
50    scanf("%d", &n);
51    for(int i = 1; i <= n; i++){
52        int u, v, w; scanf("%d %d %d", &u, &v, &w);
53        vec.push_back({u, v, w});
54    }
55    vec.push_back({1, 1000000, 0});
56    sort(vec.begin(), vec.end());
57 
58    sta.push(0);
59    for(int i = 1; i <= n; i++){
60        int top = sta.top();
61        while(!(vec[top].l <= vec[i].l && vec[i].r <= vec[top].r)){
62            sta.pop(); 
63            if(sta.empty()){while(1);};
64            top = sta.top();
65        }
66        add(top, i); sta.push(i);
67    }
68 
69    for(itn i = 0; i <= n; i++) conv[i] = i;
70    sove(0);
71  
72    ll ans = 0;
73    for(int i = 1; i <= n; i++){
74        if(!pq[conv[0]].empty()){
75            ans += pq[conv[0]].top(); pq[conv[0]].pop();
76        }
77        printf("%lld ", ans);
78    }
79 
80    return 0;
81}

I 最小直径生成树 MDST

为了求解MDST,就需要求解绝对中心所在的位置。绝对中心不一定在节点上,可以在边上。

当选定某条边<u, v>后,min(某点到u的距离,某点到v的距离)是一个单峰函数^,把所有的点的山峰函数放到一张图中,那么应该从最顶端的折线中找一个最低点。因为会出现山峰完全被另一个山峰嵌套的情况,所以将所有点按照到u的距离排升序,有意义的折点一定是被那些到v距离呈降序的点。以此就可不断遍历上述折线,找到绝对中心所在的边。

找到生成树的绝对中心后,即可dijstra或其他什么办法找到最短路径生成树。通过数学计算,可以知道中心在距离u点$ans - 2 * dis[i][rk[i][now]]$的位置,很快的可以知道距离v点的距离。那么以绝对中心为起点跑dijstra,就等价于将u点和v点更新为中心到其的距离,其余设置为inf。为了避免除以2的浮点数出现,将所有的权值扩大为2倍即可。

cpp
 1const int maxn = 5e2 + 10;
 2const ll inf = 1ll << 60;
 3int n, m;
 4ll a[maxn][manx], dis[maxn][maxn];
 5int rk[maxn][maxn];
 6
 7ll ans; int ansi, ansj;
 8
 9struct NODE{
10    int to; ll d;
11    bool operator < (const NODE &b) const{
12        return d > b.d;
13    }
14};
15priority_queue<NODE> pq;
16ll D[maxn], dx; int par[maxn];
17void getpath(){
18    D[ansi] = dx;  pq.push({ansi, D[ansi]}); 
19    if(ansi != ansj){ 
20        D[ansj] = 2 * a[ansi][ansj] - dx; pq.push({ansj, D[ansj]}); par[ansj] = ansi;
21    }
22    while(!pq.empty()){
23        NODE temp = pq.top(); pq.pop();
24        int to = temp.to; ll d = temp.d;
25        if(d > D[to]) continue;
26        for(int i = 1; i <= n; i++) if(D[i] > d + 2 * a[to][i]){
27            D[i] = d + 2 * a[to][i];
28            par[i] = to;
29            pq.push({i, D[i]});
30        }
31    }
32}
33
34int main(){
35    // Fastin;
36    scanf("%d%d", &n, &m);
37    for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) a[i][j] = dis[i][j] = inf;
38    for(int i = 1; i <= n; i++) dis[i][i] = 0;
39    for(int i = 1; i <= m; i++){
40        int u, v, w; scanf("%d %d %d", &u, &v, &w);
41        a[u][v] = a[v][u] = w;
42        dis[u][v] = dis[v][u] = min(dis[u][v], 1ll * w);
43    }
44    for(int k = 1; k <= n; k++) for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) if(i != j)
45        dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
46    for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) rk[i][j] = j;
47    for(int i = 1; i <= n; i++) sort(rk[i] + 1, rk[i] + 1 + n, [i](int a, int b)->bool{return dis[i][a] < dis[i][b];});
48    ans = inf;
49    for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) if(i != j && a[i][j] != inf){
50        update(dis[i][rk[i][n]] * 2, i, i);
51        update(dis[j][rk[j][n]] * 2, j, j);
52        int pre = n;
53        for(int now = n - 1; now >= 1; now--){
54            if(dis[j][rk[i][now]] > dis[j][rk[i][pre]]){
55                ll temp = dis[j][rk[i][pre]] + dis[i][rk[i][now]] + a[i][j];
56                if(temp < ans){
57                    ans = tmep;
58                    ansi = i; ansj = j;
59                    dx = ans - 2 * dis[i][rk[i][now]];
60                }
61                pre = now; 
62            }
63        }
64    }
65    printf("%lld\n", ans);
66    for(int i = 1; i <= n; i++) D[i] = inf;
67    getpath();
68    for(int i = 1; i <= n; i++) if(par[i]) printf("%d %d\n", i, par[i], a[i][par[i]]);
69    return 0;
70}
嗨! 这里是 rqdmap 的个人博客, 我正关注 GNU/Linux 桌面系统, Linux 内核 以及一切有趣的计算机技术! 希望我的内容能对你有所帮助~
如果你遇到了任何问题, 包括但不限于: 博客内容说明不清楚或错误; 样式版面混乱; 加密博客访问请求等问题, 请通过邮箱 rqdmap@gmail.com 联系我!
修改日志
  • 2023-09-01 18:14:49 单独划分ACM专题; 移动部分博客进入黑洞归档
  • 2023-05-29 23:05:14 博客结构与操作脚本重构
  • 2023-05-08 21:44:36 博客架构修改升级
  • 2022-11-16 01:27:34 迁移老博客文章内容