zoukankan      html  css  js  c++  java
  • 第四届CCF软件能力认证(CSP2015) 第五题(最小花费)题解

    【问题描述】

      C国共有$n$个城市。有$n-1$条双向道路,每条道路连接两个城市,任意两个城市之间能互相到达。小R来到C国旅行,他共规划了$m$条旅行的路线, 第$i$条旅行路线的起点是$s_i$,终点是$t_i$。在旅行过程中,小R每行走一单位长度的路需要吃一单位的食物。C国的食物只能在各个城市中买到,而且不同城市的食物价格可能不同。
      然而,小R不希望在旅行中为了购买较低价的粮食而绕远路,因此他总会选择最近的路走。现在,请你计算小R规划的每条旅行路线的最小花费是多少。

    【输入格式】

      第一行包含2个整数$n$和$m$。
      第二行包含$n$个整数。第$i$个整数$w_i$表示城市$i$的食物价格。
      接下来$n-1$行,每行包括3个整数$u, v, e$,表示城市$u$和城市$v$之间有一条长为$e$的双向道路。
      接下来$m$行,每行包含2个整数$s_i$和$t_i$,分别表示一条旅行路线的起点和终点。

    【输出格式】

      输出$m$行,分别代表每一条旅行方案的最小花费。

    【样例输入】

    6 4
    1 7 3 2 5 6
    1 2 4
    1 3 5
    2 4 1
    3 5 2
    3 6 1
    2 5
    4 6
    6 4
    5 6

    【样例输出】

    35
    16
    26
    13

    【样例说明】

    对于第一条路线,小R会经过2->1->3->5。其中在城市2处以7的价格购买4单位粮食,到城市1时全部吃完,并用1 的价格购买7单位粮食,然后到达终点。

    【评测用例规模与约定】

      前10%的评测用例满足:$n, m ≤ 20, w_i ≤ 20$;
      前30%的评测用例满足:$n, m ≤ 200$;
      另有40%的评测用例满足:一个城市至多与其它两个城市相连。
      所有评测用例都满足:$1 ≤ n, m ≤ 10^5,1 ≤ w_i ≤ 10^6,1 ≤ e ≤ 10000$。

    【题解】

    首先注意到,一条路径的选择方案,一定是从一个点走到下一个比它便宜的点,这之间的食物都在这个点购买。

    而这个信息不具有可加性,却具有可减性。

    之前在网上搜到了一篇自称要维护两遍单调栈三个lct的博客,但是维护单调栈的最坏时间复杂度为$O(n^2)$。

    下面介绍一种基于点分治的做法。

    假设当前分治中心为T,对于一个询问u->v,可以被拆成u->T,T->v,对于u->T的费用,我们可以在倍增数组上二分出u上面第一个比它便宜的位置,用这个位置的信息可以直接得出u的信息。

    现在考虑T->v的费用,对于每一个询问,都附加了一个状态,表示之前便宜的费用c,我们需要在T->v的路径上找到第一个比它便宜的点设为x(这个可以通过dfs时维护一个前缀最小值数据来二分求得),这一段用的费用是dis(T, x) * c,剩下的部分就是从x向下走走到v的费用,我们可以通过dfs求出每个点到T的费用,之前已经提到过,维护的信息具有可减性,就可以$O(1)$的时间算出从一个点往下走的走到某个点的费用。

    至此,问题在$O(nlog^2n)$的时间复杂度,$O(nlog n)$的空间复杂度内解决。

    【代码】(滥用stl导致常数非常大)

      1 #include<bits/stdc++.h>
      2 
      3 using namespace std;
      4 
      5 typedef long long LL;
      6 typedef pair<int, int> pii;
      7 #define FI first
      8 #define SE second
      9 #define for_edge(u, it) for(vector<pii>::iterator it = G[u].begin(); it != G[u].end(); ++it)
     10 
     11 const int N = 100000 + 10;
     12 
     13 vector<pii> G[N];
     14 vector<int> Q[N], q1[N], q2[N];
     15 LL dis[N], disv[N];
     16 int cost[N], sz[N], maxsz[N], top[N], root;
     17 pii q_info[N];
     18 pair<LL, int> ans[N];
     19 bool centre[N];
     20 
     21 #define v it->FI
     22 void get_size(int u, int fa) {
     23     maxsz[u] = 0, sz[u] = 1;
     24     for_edge(u, it) if(v != fa && !centre[v]) {
     25         get_size(v, u);
     26         sz[u] += sz[v];
     27         maxsz[u] = max(maxsz[u], sz[v]);
     28     }
     29 }
     30 
     31 void get_root(int u, int fa, int r) {
     32     maxsz[u] = max(maxsz[u], sz[r] - sz[u]);
     33     if(maxsz[u] < maxsz[root]) root = u;
     34     for_edge(u, it) if(v != fa && !centre[v]) {
     35         get_root(v, u, r);
     36     }
     37 }
     38 
     39 int anc[17][N], val[17][N];
     40 
     41 int tot_time;
     42 
     43 void get_top(int u, int fa, int pre) {
     44     anc[0][u] = fa, val[0][u] = cost[u];
     45     int tmp = clock();
     46     for(int i = 1; i < 17; i++) {
     47         val[i][u] = min(val[i-1][u], val[i-1][anc[i-1][u]]);
     48         anc[i][u] = anc[i-1][anc[i-1][u]];
     49     }
     50     tot_time += clock() - tmp;
     51         
     52     top[u] = pre;
     53     for_edge(u, it) if(v != fa && !centre[v]) {
     54         dis[v] = dis[u] + it->SE, get_top(v, u, pre);
     55     }
     56 }
     57 
     58 int pre[N];
     59 
     60 int find(int u) {
     61     int cost_u = cost[u];
     62     //int tmp = clock();
     63     for(int i = 16; i >= 0; i--) {
     64         if(val[i][u] >= cost_u) u = anc[i][u];
     65     }
     66     //tot_time += clock() - tmp;
     67     return u;
     68 }
     69 
     70 void calc_1(int u, int fa) {
     71     int anc = find(u);
     72 
     73     if(anc) pre[u] = pre[anc];
     74     else anc = root, pre[u] = u; // be root when not exist
     75     disv[u] = (dis[u] - dis[anc]) * cost[u] + disv[anc];
     76     
     77     for(unsigned i = 0; i < q1[u].size(); i++) {
     78         ans[q1[u][i]] = make_pair(disv[u], cost[pre[u]]);
     79     }
     80     
     81     for_edge(u, it) if(v != fa && !centre[v]) {
     82         calc_1(v, u);
     83     }
     84 
     85 }
     86 
     87 void calc_2(int u, int fa, int fee) {
     88     static int val[N], id[N], tot;
     89     disv[u] = (dis[u] - dis[fa]) * fee + disv[fa];
     90     id[tot] = u, val[tot] = cost[u];
     91     if(tot++) val[tot-1] = min(val[tot-2], cost[u]);
     92 
     93     id[tot] = u; // be u when not exist
     94     for(unsigned i = 0; i < q2[u].size(); i++) {
     95         int c = q2[u][i];
     96         int anc = id[lower_bound(val, val + tot, ans[c].SE, greater<int>()) - val];
     97         ans[c].FI += ans[c].SE * (dis[anc] - dis[root]) + disv[u] - disv[anc];
     98     }
     99 
    100     for_edge(u, it) if(v != fa && !centre[v]) {
    101         calc_2(v, u, min(fee, cost[u]));
    102     }
    103     --tot;
    104 }
    105 
    106 void solve(int u) {
    107     if(!Q[u].size()) return;
    108     
    109     get_size(u, 0);
    110     root = u, get_root(u, 0, u);
    111     //cerr << sz[u] << ' ' << maxsz[root] << endl;
    112     
    113     vector<int> vec_q;
    114     vec_q.swap(Q[u]);
    115 
    116     centre[u = root] = 1, top[u] = u, dis[u] = 0;
    117     for(int i = 0; i < 17; i++) anc[i][u] = val[i][u] = 0;
    118     val[0][u] = cost[u];
    119     for_edge(u, it) if(!centre[v]) {
    120         dis[v] = it->SE, get_top(v, u, v);
    121     }
    122 
    123     for(unsigned i = 0; i < vec_q.size(); i++) {
    124         int c = vec_q[i], x = q_info[c].FI, y = q_info[c].SE;
    125         if(top[x] == top[y]) Q[top[x]].push_back(c);
    126         else q1[x].push_back(c), q2[y].push_back(c);
    127     }
    128 
    129 
    130     disv[u] = 0, pre[u] = u;
    131     calc_1(u, 0);
    132 
    133     disv[u] = 0;
    134     calc_2(root, 0, cost[u]);
    135 
    136     for(unsigned i = 0; i < vec_q.size(); i++) {
    137         int c = vec_q[i], x = q_info[c].FI, y = q_info[c].SE;
    138         q1[x].clear(), q2[y].clear();
    139     }
    140 
    141     for_edge(u, it) if(!centre[v]) {
    142         solve(v);
    143     }
    144 }
    145 #undef v
    146 
    147 int main() {
    148 #ifdef DEBUG
    149     freopen("in.txt", "r", stdin);
    150     freopen("out.txt", "w", stdout);
    151     int start_time = clock();
    152 #endif
    153 
    154     int n, m; scanf("%d%d", &n, &m);
    155     for(int i = 1; i <= n; i++) {
    156         scanf("%d", cost + i);
    157     }
    158     for(int i = 1; i < n; i++) {
    159         int u, v, w;
    160         scanf("%d%d%d", &u, &v, &w);
    161         G[u].push_back(pii(v, w));
    162         G[v].push_back(pii(u, w));
    163     }
    164 
    165     for(int i = 0; i < m; i++) {
    166         pii &cur = q_info[i];
    167         scanf("%d%d", &cur.FI, &cur.SE);
    168         if(cur.FI != cur.SE) Q[1].push_back(i);
    169     }
    170 
    171     solve(1);
    172 
    173     for(int i = 0; i < m; i++) {
    174         printf("%I64d
    ", ans[i].FI);
    175     }
    176 #ifdef DEBUG
    177     fprintf(stderr, "time used : %.5fs, %.5fs
    ", (double) (clock() - start_time) / CLOCKS_PER_SEC, (double)tot_time / CLOCKS_PER_SEC);
    178 #endif
    179     return 0;
    180 }
    View Code
  • 相关阅读:
    socket 编程练习--UDP聊天程序
    练习 多线程
    练习二:求最大公约数
    练习一:单链表的反转,串的模式匹配
    【算法1】string 的全排列
    lw资料
    SDmenu滑动菜单获取导航连接部分代码
    FileOperate(个人资料)
    C/C++基本数据类型
    C++/MFC计算程序运行时间
  • 原文地址:https://www.cnblogs.com/showson/p/5597427.html
Copyright © 2011-2022 走看看