luogu1525 关押罪犯

来源:互联网 发布:大众网络报 微信 编辑:程序博客网 时间:2024/06/04 17:42

luogu1525
普及+ / 提高

题面:
N(N<20000)个点,M(M<100000)条边。每条边有一个权值。将图分成两部分,删除跨越两部分的边,使整个图内权值最大的边最小。

输入输出样例

思路:
有权并查集
1、将各边按照从大到小的顺序排序(优先队列)

struct tEdge(){    ...    inline void addEdge(...) {...}    bool operator < (const tEdge& rhs) const { retur w>rhs.w; } //重载运算符};priority_queue <tEdge> q; //优先队列int main(){    ...    tEdge tmp;    tmp.addEdge(x, y, w);    q.push(tmp);    ...}

2、并查集

struct tNode{    int fa, len, size; //fa:父亲; len:权值, 0代表和fa在同一部分, 1代表和fa不在同一部分; size:并查集的大小,用于按秩合并    inline void init(int i) { fa = i; len = 0; size = 1; } //初始化} node[MAXN];inline void makeSet(int s) { for(int i=1; i<=s; i++) node[i].init(i); } //初始化并查集

find函数,使用了路径压缩,在路径压缩时将权值更新:

inline int find(int x){    if(node[x].fa == x) return x;    int tmpfa = node[x].fa; //记录原来的father,以便更新权值    node[x].fa = find(node[x].fa); //路径压缩    node[x].len ^= node[tmpfa].len; //更新权值, 1^1 = 0, 1^0 = 1, 0^0 = 0    return node[x].fa;}

unionSet函数, 合并时也要注意更新权值:

void unionSet(int x, int y){    int rx = x, ry = y; //记录原来的x y    x = find(x); y = find(y); //找到x,y的祖先,并将路径压缩, 更新了权值    int w = node[rx].len ^ node[ry].len ^ 1; //因为要使x和y在不同部分中, 所以无论是将x合并至y还是将y合并至x, 权值都是相同的; node[rx].len^node[ry].len计算出了x与x的祖先的情况与y与y的祖先的情况是否一致, 若一致, 则x的祖先与y的祖先不在同意部分, 反之则在同意部分,故再异或上1    if(x == y) return ;    if(node[x].size > node[y].size)    {        node[y].fa = x;        node[y].len = w;        node[x].size += node[y].size;    }    else     {        node[x].fa = y;        node[x].len = w;        node[y].size += node[x].size;    } //按秩合并, 并将信息更新    return;}

3、判断最小的最大值
因为要让最大值最小,所以可以按边的权值从大到小的顺序枚举。当枚举到一条边,且该边的两个节点属于同意部分时,则这条边无可避免的被分到了同一部分中,答案即是这条边的权值:

int main(){    ...    while(!q.empty)    {        ...        int ra = a, rb = b;        a = find(a); b = find(b);        if(a == b) //如果a、b的祖先相同        {             if(node[ra].len != node[rb].len) continue; //如果a、b不在同一部分,则继续            ans = c;             break; //否则直接跳出循环        }        else unionSet(ra, rb); //如果a、b的祖先不同, 则将a与b所在的并查集合并    }    ...}
原创粉丝点击