Codeforces 827D Best Edge Weight 倍增 + 并查集 || 倍增 + 压倍增标记 (看题解)

Best Edge Weight

我们先找出一棵最小生成树,

对于非树边来说, 答案就是两点路径上的最大值 - 1, 这个直接倍增就能处理。

对于树边来说, 就是非树边的路径经过这条边的最小值 - 1, 这个可以用并查集压缩路径 或者 更压st表一样的方式更新。

感觉就是没想到先扣出来一个最小生成树, 而是往克鲁斯卡尔的过程中想了。

#include<bits/stdc++.h>
#define LL long long
#define LD long double
#define ull unsigned long long
#define fi first
#define se second
#define mk make_pair
#define PLL pair<LL, LL>
#define PLI pair<LL, int>
#define PII pair<int, int>
#define SZ(x) ((int)x.size())
#define ALL(x) (x).begin(), (x).end()
#define fio ios::sync_with_stdio(false); cin.tie(0);

using namespace std;

const int N = 2e5 + 7;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-8;
const double PI = acos(-1);

template<class T, class S> inline void add(T& a, S b) {a += b; if(a >= mod) a -= mod;}
template<class T, class S> inline void sub(T& a, S b) {a -= b; if(a < 0) a += mod;}
template<class T, class S> inline bool chkmax(T& a, S b) {return a < b ? a = b, true : false;}
template<class T, class S> inline bool chkmin(T& a, S b) {return a > b ? a = b, true : false;}

int n, m;
vector<PII> G[N];
bool can[N];
int ans[N];
int pathMin[N];

struct Edge {
    int u, v, c, id;
    bool operator < (const Edge &rhs) const {
        return c < rhs.c;
    }
} e[N];

int fa[N];
int getRoot(int x) {
    return fa[x] == x ? x : fa[x] = getRoot(fa[x]);
}

int pa[N][20], mx[N][20], depth[N];

void dfs(int u, int fa, int w) {
    depth[u] = depth[fa] + 1;
    pa[u][0] = fa; mx[u][0] = w;
    for(int i = 1; i < 20; i++)
        pa[u][i] = pa[pa[u][i - 1]][i - 1];
    for(int i = 1; i < 20; i++)
        mx[u][i] = max(mx[u][i - 1], mx[pa[u][i - 1]][i - 1]);
    for(auto& e : G[u]) {
        if(e.se == fa ) continue;
        dfs(e.se, u, e.fi);
    }
}

int getMaxWei(int u, int v) {
    if(depth[u] < depth[v]) swap(u, v);
    int dis = depth[u] - depth[v];
    int ans = 0;
    for(int i = 19; i >= 0; i--)
        if(dis >> i & 1) chkmax(ans, mx[u][i]), u = pa[u][i];
    if(u == v) return ans;
    for(int i = 19; i >= 0; i--) {
        if(pa[u][i] != pa[v][i]) {
            chkmax(ans, mx[u][i]);
            chkmax(ans, mx[v][i]);
            u = pa[u][i];
            v = pa[v][i];
        }
    }
    chkmax(ans, mx[u][0]);
    chkmax(ans, mx[v][0]);
    return ans;
}

int getLca(int u, int v) {
    if(depth[u] < depth[v]) swap(u, v);
    int dis = depth[u] - depth[v];
    for(int i = 19; i >= 0; i--)
        if(dis >> i & 1) u = pa[u][i];
    if(u == v) return u;
    for(int i = 19; i >= 0; i--)
        if(pa[u][i] != pa[v][i])
            u = pa[u][i], v = pa[v][i];
    return pa[u][0];
}

void gao(int u, int v, int c) {
    while(1) {
        u = getRoot(u);
        if(depth[u] <= depth[v]) break;
        pathMin[u] = c;
        int nex = getRoot(pa[u][0]);
        fa[u] = nex;
        u = nex;
    }
}

int main() {
    memset(pathMin, 0x3f, sizeof(pathMin));
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++) fa[i] = i;
    for(int i = 1; i <= m; i++) {
        scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].c);
        e[i].id = i;
    }
    sort(e + 1, e + 1 + m);
    for(int i = 1; i <= m; i++) {
        int u = e[i].u, v = e[i].v, c = e[i].c, id = e[i].id;
        int x = getRoot(u);
        int y = getRoot(v);
        if(x != y) {
            can[i] = true;
            fa[y] = x;
            G[u].push_back(mk(c, v));
            G[v].push_back(mk(c, u));
        }
    }

    dfs(1, 0, 0);

    for(int i = 1; i <= n; i++) fa[i] = i;
    for(int i = 1; i <= m; i++) {
        if(can[i]) continue;
        int u = e[i].u, v = e[i].v, c = e[i].c, id = e[i].id;
        int lca = getLca(u, v);
        gao(u, lca, c);
        gao(v, lca, c);
    }

    for(int i = 1; i <= m; i++) {
        int u = e[i].u, v = e[i].v, c = e[i].c, id = e[i].id;
        if(!can[i]) {
            ans[id] = getMaxWei(u, v) - 1;
        } else {
            if(depth[u] < depth[v]) swap(u, v);
            ans[id] = pathMin[u] == inf ? -1 : pathMin[u] - 1;
        }
    }

    for(int i = 1; i <= m; i++)
        printf("%d%c", ans[i], " \n"[i == m]);
    return 0;
}

/*

*/

原文地址:https://www.cnblogs.com/CJLHY/p/10956883.html

时间: 2024-10-01 10:47:40

Codeforces 827D Best Edge Weight 倍增 + 并查集 || 倍增 + 压倍增标记 (看题解)的相关文章

Codeforces Round #250 (Div. 1) B 并查集

坑!神坑!深坑!,WA了几十把,最终答案  (ans * 2)/(n * 1.0 * (n - 1)) 要是写成(ans * 2)/(n *(n - 1)*1.0)就是WA,不明白为啥,愤怒的我 全改成double就可以了,若前面变量用了int的 答案必须是前一种写法, 题目不是特别难,没啥思路画一画就有思路了,10^5的n去扫肯定是要超时的,那就想想一次性的10^5,发想通过m是可以的,建边,边权就是两端点中小的那个,然后对最终答案的种数进行分析,发现其实就是 每次你要连接的两块连通块的个数相

CodeForces 776D The Door Problem【并查集】

CodeForces 776D The Door Problem[并查集]并查集 设 f 1--m 表示 开的情况 m+1--2*m 表示关的情况 对于每盏灯 如果他 是关的 则 x--y x+m--y+m 表示要同关 或者同开 如果他 是开的 则 x+m--y x--y+m 表示一个关 一个开如果一盏灯 的 x 连向 了 x+m 则表示是矛盾了 那么久是错误的 题意:给你n个门,和m组开关,每扇门都有两个开关控制,每个开关控制x扇门,如果选择了某组开关,则使这组开关里的每个开关控制的所有的门按

【BZOJ4569】[Scoi2016]萌萌哒 倍增+并查集

[BZOJ4569][Scoi2016]萌萌哒 Description 一个长度为n的大数,用S1S2S3...Sn表示,其中Si表示数的第i位,S1是数的最高位,告诉你一些限制条件,每个条件表示为四个数,l1,r1,l2,r2,即两个长度相同的区间,表示子串Sl1Sl1+1Sl1+2...Sr1与Sl2Sl2+1Sl2+2...Sr2完全相同.比如n=6时,某限制条件l1=1,r1=3,l2=4,r2=6,那么123123,351351均满足条件,但是12012,131141不满足条件,前者数

Codeforces Round #541 (Div. 2) D 并查集 + 拓扑排序

https://codeforces.com/contest/1131/problem/D 题意 给你一个n*m二维偏序表,代表x[i]和y[j]的大小关系,根据表构造大小分别为n,m的x[],y[],使得两个数组中最大的数尽量小 题解 按照偏序表,构造出从小到大的拓扑图 如何解决相等的数的偏序关系? 用并查集缩点后再进行拓扑排序 如何解决最大的数最小? 只需要使得同一层的数相同就行,可以一批处理栈中的元素,对于一批栈中的元素产生的新点,先放进一个容器里,然后等到这批栈清空了,再把这个容器中的点

[SCOI2016]萌萌哒(倍增+并查集)

当区间\([a,b]\)和\([c,d]\)对应相等时. 我们把两个区间对应位置上的数所在并查集合并. 最后并查集的数量为\(num\)答案就是\(9*10^num\)因为是个数,不能有前置\(0\). 但是两个区间对应位置上的数所在并查集合并太浪费时间. 怎么办. 考虑使用倍增. 我们用\((i,j)\)代表\([i,i+(1<<j)-1]\)这个区间然后任何一个区间最多可以\(log\)个这样的倍增的区间拼起来. 然后呢? 我们按倍增区间的大小从大往小枚举.当\((x,i)\)和\((y,

CodeForces 745C Hongcow Builds A Nation 并查集

题意: 给了你n个城市 m条边 k个政府 每个政府管辖的区域内不能和其他政府的区域有相连 即政府之间不存在路径 问你在维护这种关系的同时 最多再加多少条边 思路: 先找出来每个联通块 再找出来没有归属的孤立的点 把他们都放到最大的联通块里 然后每个联通块之间的点两两连边是n*(n-1)/2条边 最后算出来的ans-m就好了 (看别人的博客学了一个max_element 1 #include<bits/stdc++.h> 2 #define cl(a,b) memset(a,b,sizeof(a

codeforces 691D Swaps in Permutation(并查集)

题意: 给你一个长度为n的数列,然后给你m组数, 表示这两个数可以交换 然后让你给出字典序最大的数列 思路: 用并查集,可交换的数都是成组的,把同一并查集中的数加在根节点的vector后, 在一个并查集中的数,从大到输出就好了 /* *********************************************** Author :devil ************************************************ */ #include <cstdio>

CodeForces 698B Fix a Tree (并查集应用)

当时也是想到了并查集,但是有几个地方没有想清楚,所以就不知道怎么写了,比如说如何确定最优的问题.赛后看了一下别人的思路,才知道自己确实经验不足,思维也没跟上. 其实没有那么复杂,这个题目我们的操作只有三个 1.确定根节点.2.解环. 3连接子树. 如果题目中给出了一个或者多个根节点,我们任选一个即可,证明:假设有k个可行根节点,那么选择一个不动,改动k-1次,每种选择都是这样.但如果题目中没有可选根节点,就不可以随便去选了,首先明确这种情况一定存在了1个或者多个环,我们一定要从环中选取根节点,因

[Codeforces 1027 F] Session in BSU [并查集维护二分图匹配问题]

题面 传送门 思路 真是一道神奇的题目呢 题目本身可以转化为二分图匹配问题,要求右半部分选择的点的最大编号最小的一组完美匹配 注意到这里左边半部分有一个性质:每个点恰好连出两条边到右半部分 那么我们可以利用这个性质 考虑一个左边的点和它右边联通的两个点,发现这两个点只能选择一个和这个左边的点匹配 那么我们考虑把这个点点匹配的模型转化成点边匹配 我们在同一个左边点连的两个右边点之间连边,那么问题就变成了一个点和一条相邻的边匹配,求完美匹配的问题了 而这个问题,我们显然可以用并查集来很好的解决 考虑