[HDU 3712] Fiolki (带边权并查集+启发式合并)

[HDU 3712] Fiolki (带边权并查集+启发式合并)

题面

化学家吉丽想要配置一种神奇的药水来拯救世界。
吉丽有n种不同的液体物质,和n个药瓶(均从1到n编号)。初始时,第i个瓶内装着g[i]克的第i种物质。吉丽需要执行一定的步骤来配置药水,第i个步骤是将第a[i]个瓶子内的所有液体倒入第b[i]个瓶子,此后第a[i]个瓶子不会再被用到。瓶子的容量可以视作是无限的。
吉丽知道某几对液体物质在一起时会发生反应产生沉淀,具体反应是1克c[i]物质和1克d[i]物质生成2克沉淀,一直进行直到某一反应物耗尽。生成的沉淀不会和任何物质反应。当有多于一对可以发生反应的物质在一起时,吉丽知道它们的反应顺序。每次倾倒完后,吉丽会等到反应结束后再执行下一步骤。
吉丽想知道配置过程中总共产生多少沉淀。

\(n,m \leq 2 \times 10^5,k \leq 5 \times 10^5\)

分析

注意到初始状态下第i个瓶子里有物质i,也就是说每种物质恰好只在一个瓶子里。那么混合的过程中,每种反应至多发生一次。对于一个反应\((a,b)\),因为开始反应前只有1个瓶子里有a,1个瓶子里有b.而当a,b相遇时,会一直进行直到完全反应。

那么,我们只要知道第i个反应发生的时间,然后按时间给反应排序(时间相同时按优先级排序).然后一个个反应按顺序模拟,更新反应物的质量和沉淀质量。

如何求某个反应(a,b)发生的时间呢?。我们把处在同一个烧杯里的物质看成一个联通块,(a,b)发生的时间就是a和b最早连通的时间。用并查集维护连通性,每个点x还要另外记录tim[x],表示x什么时间与父亲相连。答案就是u到v路径上的点tim的最大值。具体参考[BZOJ 4668]冷战(并查集+启发式合并)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 200000
#define maxk 500000
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
struct disjoint_set{
    int fa[maxn+5];
    int tim[maxn+5];
    int sz[maxn+5];
    int find(int x){
        while(fa[x]!=x) x=fa[x];
        return x;
    }
    int get_deep(int x){
        int ans=0;
        while(fa[x]!=x){
            ans++;
            x=fa[x];
        }
        return ans;
    }
    void merge(int x,int y,int t){
        int fx=find(x);
        int fy=find(y);
        if(sz[fx]>sz[fy]) swap(fx,fy);
        fa[fx]=fy;
        tim[fx]=t;
        sz[fy]+=sz[fx];
    }
    int query(int x,int y){
        if(find(x)!=find(y)) return INF;
        int ans=0;
        int dx=get_deep(x),dy=get_deep(y);
        if(dx<dy){
            swap(x,y);
            swap(dx,dy);
        }
        while(dx>dy){
            ans=max(ans,tim[x]);
            x=fa[x];
            dx--;
        }
        if(x==y) return ans;
        while(x!=y){
            ans=max(ans,max(tim[x],tim[y]));
            x=fa[x];
            y=fa[y];
        }
        return ans;
    }
    void ini(int n){
        for(int i=1;i<=n;i++){
            fa[i]=i;
            sz[i]=1;
        }
    }
}S;
int n,m,k;
int g[maxn+5];
struct rec{
    int x;
    int y;
    int tim;
    int id;
    friend bool operator < (rec p,rec q){
        if(p.tim==q.tim) return p.id<q.id;
        else return p.tim<q.tim;
    }
}q[maxk+5];
int main(){
    int u,v;
    scanf("%d %d %d",&n,&m,&k);
    for(int i=1;i<=n;i++){
        scanf("%d",&g[i]);
    }
    S.ini(n);
    for(int i=1;i<=m;i++){
        scanf("%d %d",&u,&v);
        S.merge(u,v,i);
    }
    for(int i=1;i<=k;i++){
        scanf("%d %d",&q[i].x,&q[i].y);
        q[i].tim=S.query(q[i].x,q[i].y);
        q[i].id=i;
    }
    sort(q+1,q+1+k);
    ll ans=0;
    for(int i=1;i<=k;i++){
        if(q[i].tim==INF) continue;
        int x=q[i].x,y=q[i].y;
        int sum=min(g[x],g[y]);
        g[x]-=sum;
        g[y]-=sum;
        ans+=sum*2;
    }
    printf("%lld\n",ans);
}

原文地址:https://www.cnblogs.com/birchtree/p/11517009.html

时间: 2024-10-07 21:14:30

[HDU 3712] Fiolki (带边权并查集+启发式合并)的相关文章

HDU-3038 How Many Answers Are Wrong(带权并查集区间合并)

http://acm.hdu.edu.cn/showproblem.php?pid=3038 大致题意: 有一个区间[0,n],然后会给出你m个区间和,每次给出a,b,v,表示区间[a,b]的区间和为v,但每次给出的区间可能与之前的有冲突,问这样起冲突的区间共有多少个 首先区间[a,b]的和可由区间[0,b]的和减去区间[0,a-1]的和得到 但是我们不太可能知道[0,b],故我们只用知道和b的合并过的区间的左端点就行 其实并查集实质就是一颗树,我们可以以树的角度去看待它,理解维护过程 不理解的

【BZOJ-4668】冷战 并查集 + 启发式合并 + 乱搞

4668: 冷战 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 37  Solved: 24[Submit][Status][Discuss] Description 1946 年 3 月 5 日,英国前首相温斯顿·丘吉尔在美国富尔顿发表“铁幕演说”,正式拉开了冷战序幕. 美国和苏联同为世界上的“超级大国”,为了争夺世界霸权,两国及其盟国展开了数十年的斗争.在这段时期,虽然分歧和冲突严重,但双方都尽力避免世界范围的大规模战争(第三次世界大战)爆发

并查集例题02.带权并查集(poj1182)

Description 动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形.A吃B, B吃C,C吃A.现有N个动物,以1-N编号.每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种.有人用两种说法对这N个动物所构成的食物链关系进行描述:第一种说法是"1 X Y",表示X和Y是同类.第二种说法是"2 X Y",表示X吃Y.此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的.当一句话满足下列三条之一时,这句

HDU 3047 Zjnu Stadium 带权并查集

题目来源:HDU 3047 Zjnu Stadium 题意:给你一些人 然后每次输入a b c 表示b在距离a的右边c处 求有多少个矛盾的情况 思路:用sum[a] 代表a点距离根的距离 每次合并时如果根一样 判断sum数组是否符合情况 根不一样 合并两棵树 这里就是带权并查集的精髓 sum[y] = sum[a]-sum[b]+x 这里y的没有合并前b的根 #include <cstdio> #include <cstring> using namespace std; cons

HDU - 3038 How Many Answers Are Wrong (带权并查集)

题意:n个数,m次询问,每次问区间a到b之间的和为s,问有几次冲突 思路:带权并查集的应用,[a, b]和为s,所以a-1与b就可以确定一次关系,通过计算与根的距离可以判断出询问的正确性 #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int MAXN = 200010; int f[MAXN],a

HDU 5176 The Experience of Love (带权并查集 + 贪心)

The Experience of Love Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 275    Accepted Submission(s): 111 Problem Description A girl named Gorwin and a boy named Vivin is a couple. They arrived

【带权并查集】HDU 3047 Zjnu Stadium

http://acm.hdu.edu.cn/showproblem.php?pid=3047 [题意] http://blog.csdn.net/hj1107402232/article/details/9921311 [Accepted] 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<string> 5 #include<cmath> 6 #incl

HDU 3172 Virtual Friends(带权并查集)

题目地址:HDU 3172 带权并查集水题.每次合并的时候维护一下权值.注意坑爹的输入.. 代码如下: #include <iostream> #include <cstdio> #include <string> #include <cstring> #include <stdlib.h> #include <math.h> #include <ctype.h> #include <queue> #inclu

hdu 5441 Travel 离线带权并查集

Travel Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=5441 Description Jack likes to travel around the world, but he doesn’t like to wait. Now, he is traveling in the Undirected Kingdom. There are n cities and m