首页 > 其他分享 >题解 P3894【[GDOI2014] 传送】

题解 P3894【[GDOI2014] 传送】

时间:2023-10-09 23:55:18浏览次数:33  
标签:dp2 dp1 int 题解 P3894 near val GDOI2014 dis

难倒不难,128MB 的空间限制快恶心死我了。

我们设 \(d_{u_0,u_1}\) 表示到节点 \((u_0,u_1)\) 距离最近的叶子的距离,这个可以很容易换根 DP 求出。设 \(p_{u_0}\) 表示树 \(u_0\) 中距离最近的两个叶子的距离。设 \(dis(u_0,u_1,v_0,v_1)\)(\(u_0=v_0\))表示树中两个节点 \(u_1\) 和 \(v_1\) 之间的距离。

(一)若 \(s_0\ne e_0\),不妨设 \(s_0 < e_0\)。

显然,在第 \(s_0\) 棵树上,从 \((s_0,s_1)\) 花费 \(d_{s_0,s_1}\) 的代价走到最近的叶子是最优的。第 \(e_0\) 棵树同理。对于 \([s_0+1,e_0-1]\) 范围内的树 \(i\),我们会花费 \(p_i\) 的代价选择最近的两个叶子经过。在两棵树之间传送时,还会产生 \(1\) 的代价。

因此,这种情况的答案为:

\[ans=d_{s_0,s_1}+\sum\limits_{i=s_0+1}^{e_0-1}p_i+d_{e_0,e_1}+(e_0-s_0) \]

(二)若 \(s_0=e_0\)。

容易想到树上两点间距离。但这是错的,还可能从 \(s_0\) 走到叶子,传送到相邻的某棵树,经过最近的两个叶子,再传送回来走到 \(e_0\)。

因此,这种情况的答案为:

\[ans=\min\{dis(s_0,s_1,e_0,e_1),d_{s_0,s_1}+p_{s_0-1}+d_{e_0,e_1}+2,d_{s_0,s_1}+p_{s_0+1}+d_{e_0,e_1}+2\} \]

你可能会有疑问:万一离 \((s_0,s_1)\) 和 \((e_0,e_1)\) 最近的叶子是同一个,怎么办呢?容易发现,这种情况下从 \((s_0,s_1)\) 和 \((e_0,e_1)\) 分别走到叶子的路径拼起来已经是 \(dis(s_0,s_1,e_0,e_1)\) 了,但还需要经过更多的边,这种情况自然更劣,不会作为 \(\min\) 保留。

时间复杂度 \(O(\sum m_i+q\log\sum m_i)\)。

本题特别卡空间,建议使用树剖 LCA 而不是倍增 LCA,数组能不开 long long 就不要开。

// Problem: P3894 [GDOI2014] 传送
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3894
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

//By: OIer rui_er
#include <bits/stdc++.h>
#define rep(x, y, z) for(int x = (y); x <= (z); ++x)
#define per(x, y, z) for(int x = (y); x >= (z); --x)
#define debug(format...) fprintf(stderr, format)
#define fileIO(s) do {freopen(s".in", "r", stdin); freopen(s".out", "w", stdout);} while(false)
#define endl '\n'
using namespace std;
typedef long long ll;

mt19937 rnd(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
int randint(int L, int R) {
    uniform_int_distribution<int> dist(L, R);
    return dist(rnd);
}

template<typename T> void chkmin(T& x, T y) {if(x > y) x = y;}
template<typename T> void chkmax(T& x, T y) {if(x < y) x = y;}

const int N = 3.5e5 + 5, M = 1e6 + 5, inf = 1e9 + 10;
const ll Inf = 2e12;

int n, m[N], q, L[N], R[N], dis[M], fa[M], sz[M], son[M], top[M], val[M], dp1[M], dp2[M];
ll near[N];
vector<tuple<int, int>> e[M];

void dfs1(int u, int f, int id) {
    fa[u] = f;
    sz[u] = 1;
    for(auto [v, w] : e[u]) {
        if(v != f) {
            dis[v] = dis[u] + 1;
            val[v] = val[u] + w;
            dfs1(v, u, id);
            chkmin(near[id], 1LL * dp1[u] + dp1[v] + w);
            chkmin(dp1[u], dp1[v] + w);
            sz[u] += sz[v];
            if(sz[v] > sz[son[u]]) son[u] = v;
        }
    }
    if(!son[u]) dp1[u] = 0;
}

void dfs2(int u, int f, int tp) {
    top[u] = tp;
    if(!son[u]) return;
    for(auto [v, w] : e[u]) {
        if(v != f) {
            dp2[v] = min(dp1[v], dp2[u] + w);
            dfs2(v, u, v == son[u] ? tp : v);
        }
    }
}

int LCA(int u, int v) {
    while(top[u] != top[v]) {
        if(dis[top[u]] < dis[top[v]]) swap(u, v);
        u = fa[top[u]];
    }
    if(dis[u] < dis[v]) swap(u, v);
    return v;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cin >> n;
    rep(i, 1, n) {
        cin >> m[i];
        L[i] = R[i - 1] + 1;
        R[i] = L[i] + m[i] - 1;
        rep(j, 1, m[i] - 1) {
            int u, v, w;
            cin >> u >> v >> w;
            e[L[i] + u].emplace_back(L[i] + v, w);
            e[L[i] + v].emplace_back(L[i] + u, w);
        }
        near[i] = Inf;
        rep(j, L[i], R[i]) dp1[j] = inf;
        dis[L[i]] = 1; val[L[i]] = 0;
        dfs1(L[i], 0, i);
        if(near[i] >= inf) near[i] = Inf;
        near[i] += near[i - 1];
        dp2[L[i]] = dp1[L[i]];
        dfs2(L[i], 0, L[i]);
    }
    for(cin >> q; q; --q) {
        int x, u, y, v;
        cin >> x >> u >> y >> v;
        ++x; ++y;
        if(x > y) swap(x, y), swap(u, v);
        u = L[x] + u;
        v = L[y] + v;
        if(x == y) {
            int p = LCA(u, v);
            ll ans = val[u] + val[v] - 2 * val[p];
            if(x > 1 && near[x - 1] - near[x - 2] < Inf) chkmin(ans, dp2[u] + dp2[v] + (near[x - 1] - near[x - 2]) + 2);
            if(x < n && near[x + 1] - near[x] < Inf) chkmin(ans, dp2[u] + dp2[v] + (near[x + 1] - near[x]) + 2);
            cout << ans << endl;
        }
        else {
            ll ans = dp2[u] + (near[y - 1] - near[x]) + dp2[v] + (y - x);
            cout << (ans >= Inf ? -1 : ans) << endl;
        }
    }
    return 0;
}

标签:dp2,dp1,int,题解,P3894,near,val,GDOI2014,dis
From: https://www.cnblogs.com/ruierqwq/p/LG-P3894.html

相关文章

  • 【ABC322C】题解
    AtCoderBeginnerContest322ProblemC-Festival题解Meaning-题意简述给定\(N\)和\(M\),还有\(M\)个正整数\(a_1\sima_n\),对于每个\(i\len\),求出\(a\)中第一个大于等于\(i\)的整数和\(i\)的差。Solution-题解思路题目保证\(a\)数组单增,所以就可......
  • 【LG-P7617】题解
    题解思路不用关心每个数的每一位是什么、哪几位相同,我们只需记录每个数出现了哪几个数字,可以使用类似于状态压缩的思想记录每个数的状压形式,比如一个数为\((4)_{10}\),那么他的状态压缩形式为\((00001)_2\)。当两个数在状态压缩表示下有一位相同,我们就认为这两个数是一对,每个......
  • 【ABC322D】题解
    AtCoderBeginnerContest322ProblemD-Polyomino题解Meaning-题意简述给定三个字符矩阵,求它们能不能拼在一起变成一个\(4\times4\)的全部是#的矩阵。Solution-题解思路大模拟。说简单也不简单,很复杂;但是说难呢,又不难。思路:搜索每一个矩阵的状态。0x001旋......
  • P1220 关路灯 题解
    Description给定\(n\)个点的位置\(a_i\)和每秒的花费\(b_i\),你的初始位置是\(s\),你删掉一个点的时间为\(0\)秒,走\(1\)个单位长度的时间是\(1\)秒。请你确定一种关灯顺序,使得所有点的最终花费最小(删掉点后这个点不会再花费)。Solution每删掉一个点,有两种选择:继续往前......
  • 汉诺塔(河内塔)题解
    汉诺塔(河内塔)题解我们定义\(T_n\)为根据规则将\(n\)个圆盘从一根柱子移动到另一根柱子的最少移动步数,按照这样的定义,本道题的答案实际上就是\(T_n\)。通过手动模拟,可得到\(T_1=1,T_2=3\)。同时显然有\(T_0=0\),即表示\(0\)个圆盘根本无需做任何移动。接着我们开始考虑......
  • CF1142D Foreigner题解
    CF1142DForeigner题解前言:题目含义真的好难理解呜呜。遇到的dp套dp的第三题,所以深入进行了理解。参考博文:https://www.cnblogs.com/AWhiteWall/p/16479483.html题意简化:先定义了不充分。首先数字$[1,9]$都不充分,注意没有$0$。当这个数字(设为$x$)大于等于$10$时......
  • 题解 CF457F 【An easy problem about trees】
    尝试理解,感谢cz_xuyixuan的题解。算作是很多情况的补充说明。我们不妨先二分答案,将\(\gemid\)的设为\(1\),\(<mid\)的设为\(0\),于是问题转化为了权值均为\(0/1\)的版本。我们称一棵树的大小为其非叶节点数。我们称一棵大小为奇数的树为奇树,大小为偶数的树为偶树。对......
  • 题解 - CF1972E - Divisors and Table
    这题正解是虚树,本解法卡常,仅适合不会虚树的。(例如本人)注意:下文中根节点深度定义为1.第一步:转化问题我们把$g(x,y,z)$拆开,考虑每个质数是哪些点的因子。包含这个质数的点构成一个点集,我们只需求这个点集S的$\sum\limits_{x,y,z\inS}f(x,y,z)$。第二步:对......
  • P4801题解
    解题思路:确实是一道很好的贪心,但由于加上了水这个影响因素,使题目复杂度上升了不少。(考虑的东西多了嘛)输个入。对饼干温度无脑排序。求最小值。求最大值(用双指针做,后面会讲)。解题过程:先输入(这个步骤就不用我讲了)inta[1000005];longlongn,ws;longlongmin......
  • Codeforces Round 902 (Div. 2) (CF1877) B、C、D 题解
    B题目大意你要传话给\(n\)个人,每传一下话需要花费\(p\),当一个人被传话后,他可以最多传给\(a_i\)个人,每次花费\(b_i\)。问把话传给\(n\)个人的最小花费。分析首先传给第一个人只少要\(p\)下来贪心,每次让花费最小、且能够传话的人去传话。考虑建一个堆,堆内的信息是......