[CF Round #278] Tourists

给定一个n个点m条边的无向图,求图上的两点的所有的简单路径之间的最小边。

$ n,m,q \leq 100000, w_i \leq 10 ^7$

## Solution

考虑建立用缩点双来建立广义圆方树,然后方点的值是当前点双内最小的点 ,这样就直接维护树上的最小值就可以了。 但如果更新的是根节点。那么他的每个方儿子都会被更新。所以特别处理一下根节点即可。记方点权值为除根节点外的点。特别处理一下即可。

## Code

```cpp
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
#define drep(i, a, b) for(int i = (a), i##_end_ = (b); i >= i##_end_; --i)
#define clar(a, b) memset((a), (b), sizeof(a))
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define Debug(s) debug("The massage in line %d, Function %s: %s\n", __LINE__, __FUNCTION__, s)
typedef long long LL;
typedef long double LD;
const int BUF_SIZE = (int)1e6 + 10;
struct fastIO {
    char buf[BUF_SIZE], buf1[BUF_SIZE];
    int cur, cur1;
    FILE *in, *out;
    fastIO() {
        cur = BUF_SIZE, in = stdin, out = stdout;
        cur1 = 0;
    }
    inline char getchar() {
        if(cur == BUF_SIZE) fread(buf, BUF_SIZE, 1, in), cur = 0;
        return *(buf + (cur++));
    }
    inline void putchar(char ch) {
        *(buf1 + (cur1++)) = ch;
        if (cur1 == BUF_SIZE) fwrite(buf1, BUF_SIZE, 1, out), cur1 = 0;
    }
    inline int flush() {
        if (cur1 > 0) fwrite(buf1, cur1, 1, out);
        return cur1 = 0;
    }
}IO;
#define getchar IO.getchar
#define putchar IO.putchar
int read() {
    char ch = getchar();
    int x = 0, flag = 1;
    for(;!isdigit(ch); ch = getchar()) if(ch == ‘-‘) flag *= -1;
    for(;isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    return x * flag;
}
void write(int x) {
    if(x < 0) putchar(‘-‘), x = -x;
    if(x >= 10) write(x / 10);
    putchar(x % 10 + 48);
}
void putString(char s[], char EndChar = ‘\n‘) {
    rep(i, 0, strlen(s) - 1) putchar(*(s + i));
    if(~EndChar) putchar(EndChar);
}

#define Maxn 300009
struct edge {
    int to, nxt;
}g[Maxn << 1], g1[Maxn << 1];
int n, m, head[Maxn], e, e1, head1[Maxn];
multiset<int> S[Maxn];
int val[Maxn];
int dfn[Maxn], low[Maxn], _clk, cnt_Squ;
stack <int> s;
int top[Maxn], dep[Maxn], size[Maxn], son[Maxn];
int fa[Maxn], efn[Maxn], _index;
namespace INIT {
    void add(int u, int v) {
        g[++e] = (edge){v, head[u]}, head[u] = e;
    }
    void add1(int u, int v) {
        g1[++e1] = (edge){v, head1[u]}, head1[u] = e1;
    }
    void tarjan(int u, int fa) {
        dfn[u] = low[u] = ++_clk;
        s.push(u);
        for(int i = head[u]; ~i; i = g[i].nxt) {
            int v = g[i].to;
            if(v != fa)
                if(!dfn[v]) {
                    tarjan(v, u);
                    low[u] = min(low[u], low[v]);
                    if(low[v] >= dfn[u]) {
                        add1(n + (++cnt_Squ), u), add1(u, n + cnt_Squ);
                        int a;
                        do {
                            a = s.top(); s.pop();
                            add1(n + cnt_Squ, a), add1(a, n + cnt_Squ);
                        }while(a != v);
                    }
                }else low[u] = min(low[u], dfn[v]);
        }
    }
    void dfs_init(int u, int f) {
        size[u] = 1;
        dep[u] = dep[f] + 1, fa[u] = f;
        for(int i = head1[u]; ~i; i = g1[i].nxt) {
            int v = g1[i].to;
            if(v != f) {
                dfs_init(v, u);
                size[u] += size[v];
                if(son[u] == -1 || size[son[u]] < size[v]) v = son[u];
            }
        }
    }
    void dfs_link(int u, int _top) {
        top[u] = _top;
        dfn[u] = ++_index, efn[_index] = u;
        if(~son[u]) dfs_link(son[u], _top);
        for(int i = head1[u]; ~i; i = g1[i].nxt) {
            int v = g1[i].to;
            if(v ^ fa[u] && v ^ son[u]) dfs_link(v, v);
        }
    }
    void Main() {
        clar(head, -1);
        clar(head1, -1);
        n = read(), m = read();
        rep(i, 1, n) val[i] = read();
        rep(i, 1, m) {
            int u = read(), v = read();
            add(u, v), add(v, u);
        }
        tarjan(1, 0);
        clar(dfn, 0), _clk = 0;
        clar(son, -1);
        dfs_init(1, 0);
        dfs_link(1, 1);
        rep(i, 2, n) S[fa[i] - n].insert(val[i]);
    }
}
namespace SGMT_tree {
    int tree[Maxn << 2];
#define lc(x) ((x) << 1)
#define rc(x) ((x) << 1 | 1)
#define ls rt << 1, l, mid
#define rs rt << 1 | 1, mid + 1, r
    void pushup(int root) {
        tree[root] = min(tree[lc(root)], tree[rc(root)]);
    }
    void build(int rt, int l, int r) {
        if(l == r) {
            tree[rt] = (efn[l] <= n) ? (val[efn[l]]) : (*S[efn[l] - n].begin());
            return ;
        }
        int mid = (l + r) >> 1;
        build(ls), build(rs);
        pushup(rt);
    }
    void modify(int rt, int l, int r, int pos) {
        if(l == r) {
            tree[rt] = (efn[l] <= n) ? (val[efn[l]]) : (*S[efn[l] - n].begin());
            return;
        }
        int mid = (l + r) >> 1;
        (pos <= mid) ? modify(ls, pos) : modify(rs, pos);
        pushup(rt);
    }
    int query(int rt, int l, int r, int x, int y) {
        if(x <= l && r <= y) return tree[rt];
        int mid = (l + r) >> 1;
        if(mid >= y) return query(ls, x, y);
        if(mid + 1 <= x) return query(rs, x, y);
        return min(query(ls, x, y), query(rs, x, y));
    }
#undef lc
#undef rc
#undef ls
#undef rs
}
namespace SOLVE {
    void Main() {
        int amt = cnt_Squ + n;
        SGMT_tree :: build(1, 1, amt);
        rep(i, 1, read()) {
            char s = getchar();
            int x = read(), y = read();
            if(s == ‘C‘) {
                if(x > 1) {
                    S[fa[x] - n].erase(S[fa[x] - n].lower_bound(val[x]));
                    S[fa[x] - n].insert(y);
                    SGMT_tree :: modify(1, 1, amt, dfn[fa[x]]);
                }
                val[x] = y;
                SGMT_tree :: modify(1, 1, amt, dfn[x]);
            }
            if(s == ‘Q‘) {
                int Tmp = x, res = INT_MAX;
                while(top[x] != top[y]) {
                    if(dep[top[x]] < dep[top[y]]) swap(x, y);
                    res = min(res, SGMT_tree :: query(1, 1, amt, dfn[top[x]], dfn[x]));
                    x = fa[top[x]];
                }
                if(dep[x] < dep[y]) swap(x, y);
                res = min(res, SGMT_tree :: query(1, 1, amt, dfn[x], dfn[y]));
                if(x > n) res = min(res, val[fa[x]]);
                assert(res != INT_MAX);
                write(val[Tmp] - res), putchar(‘\n‘);
            }
        }
    }
}
int main() {
    INIT :: Main();
    SOLVE :: Main();
#ifdef Qrsikno
    debug("\nRunning time: %.3lf(s)\n", clock() * 1.0 / CLOCKS_PER_SEC);
#endif
    return IO.flush();
}
```

原文地址:https://www.cnblogs.com/qrsikno/p/9812197.html

时间: 2024-10-14 03:09:18

[CF Round #278] Tourists的相关文章

UOJ 30 【CF Round #278】Tourists

Tourists UOJ 30 题目大意 \(n\) 个 \(m\) 条边的无向图,每个点有点权 \(w_i\) , \(q\) 次询问,每次修改第 \(a\) 个点的点权为 \(w\) ,或者查询 \(a\) 到 \(b\) 所有路径中,最小的点权 数据范围 \(1 \le n,m,q \le 10^5, 1 \le w_i \le 10^9\) 时空限制 2s,256MB 分析 对于这种在无向图上查询路径信息的题,一般利用圆方树将其转为树上问题 对于一个方点,维护点双中最小的点权,但是如果这

Codeforces Round #278 (Div. 2) b

/**  *  * @brief Codeforces Round #278 (Div. 2) b  * @file b.c  * @author mianma  * @created 2014/11/24 17:52  * @edited  2014/11/18 17:52  * @type brute  *   * @note   *          declare k >= 0;  *              then   *                  x1 = k  *   

Codeforces Round #278 (Div. 2) d

/**  * @brief Codeforces Round #278 (Div. 2) d  * @file d.c  * @author 面码  * @created 2014/11/26 10:07  * @edited  2014/11/26 10:07  * @type dp   * @note  *      自己的TL了,看了别人代码写的  *      该代码主要是在dp的基础上使用stl来提速  *      dp需辅助提速,但内存又不能爆掉是该题目的卡点 = =  */ #i

Codeforces Round #278 (Div. 2) c

/**  * @brief Codeforces Round #278 (Div. 2) c  * @file c.c  * @author 面码  * @created 2014/11/25 14:15  * @edited  2014/11/25 14:15  * @type brute  *  */ #include <stdio.h> #define max(a, b)  ((a) > (b) ? (a) : (b)) #define min(a, b)  ((a) > (

CF Round 594

CF Round 594(Div1) (A~D)简要题解 开学基本打不了cf了啊.. A Ivan the Fool and the Probability Theory 对于 $ 1 \times n $ 的情况,稍微推一推式子发现是斐波那契数列的两倍(因为第一个位置可以是0可以是1,就是两倍了,否则是一倍). 考虑第一行,第一行有两种情况: 如果第一行是 01010... 交错的,那么 0 开头可以看成一种颜色,1 开头可以看成一种颜色.然后就成了一个竖着的 $ 1 \times n $ 的

CF Round #629

CF Round #629 A.数学 给定a,b,现在问你最小让a加多少使得a能被b整除,可以为0 即算(b-(a%b))%b B.数学 给定n和k 问以n-2个a和2个b组成的串中,以字典序升序排列,问第k个是几 这个有点类似康托展开,这个简化了很多 首先考虑第一个b,它处在从左往右数第p位,那么无论第二个b怎么放,它最大是(p-1)*p/2 所以只要找到第一个b为u,第二个b从u-1开始,每往后移一位就小一,找k即可 C.数学 给定n和x x是一串开头必为2,由0,1,2组成的字符串,一共有

cf Round#273 Div.2

题目链接,点击一下 Round#273 Div.2 ================== problem A Initial Bet ================== 很简单,打了两三场的cf第一次在10分钟内过题 判平均数,且注意b为正 1 #include<iostream> 2 using namespace std; 3 int main() 4 { 5 int res = 0,n; 6 for(int i = 0; i < 5; i++) 7 { 8 cin>>

【codeforces】【比赛题解】#915 Educational CF Round 36

虽然最近打了很多场CF,也涨了很多分,但是好久没写CF的题解了. 前几次刚刚紫名的CF,太伤感情了,一下子就掉下来了,不懂你们Div.1. 珂学的那场我只做了第一题--悲伤. 这次的Educational Round打的还可以,虽然吧没有涨分(因为我是紫色的啊). 做了前4题,后面3题也比较简单,陆续也做完了. 所以心情好,来写一篇题解! [A]花园 题意: 长度为\(k\)的线段,用若干个长度为\(a_i\)的线段,正好覆盖.(\(a_i|k\)) 给定\(n\)个\(a_i\),求出最小的\

【codeforces】【比赛题解】#854 CF Round #433 (Div.2)

cf一如既往挺丧 看丧题点我! [A]分数 Petya是数学迷,特别是有关于分数的数学.最近他学了所谓一个分数被叫做"真分数"当且仅当其分子小于分母,而一个分数被叫做"最简分数"当且仅当其分子分母互质.在闲暇时间,Petya在用计算器研究:如何把最简真分数转换为小数等问题.有一天他不小心把除号(÷)按成了加号(+),导致他得到了分子与分母的和.Petya想要得到他原来的分数,但他很快发现这不是唯一的.所以现在他想要知道最大的最简真分数使得其分子与分母的和为n. 输入