bzoj 3712: [PA2014]Fiolki

Description

化学家吉丽想要配置一种神奇的药水来拯救世界。
吉丽有n种不同的液体物质,和n个药瓶(均从1到n编号)。初始时,第i个瓶内装着g[i]克的第i种物质。吉丽需要执行一定的步骤来配置药水,第i个步骤是将第a[i]个瓶子内的所有液体倒入第b[i]个瓶子,此后第a[i]个瓶子不会再被用到。瓶子的容量可以视作是无限的。
吉丽知道某几对液体物质在一起时会发生反应产生沉淀,具体反应是1克c[i]物质和1克d[i]物质生成2克沉淀,一直进行直到某一反应物耗尽。生成的沉淀不会和任何物质反应。当有多于一对可以发生反应的物质在一起时,吉丽知道它们的反应顺序。每次倾倒完后,吉丽会等到反应结束后再执行下一步骤。
吉丽想知道配置过程中总共产生多少沉淀。
Input
第一行三个整数n,m,k(0<=m<n<=200000,0<=k<=500000),分别表示药瓶的个数(即物质的种数),操作步数,可以发生的反应数量。
第二行有n个整数g[1],g[2],…,g[n](1<=g[i]<=10^9),表示初始时每个瓶内物质的质量。
接下来m行,每行两个整数a[i],bi,表示第i个步骤。保证a[i]在以后的步骤中不再出现。
接下来k行,每行是一对可以发生反应的物质c[i],di,按照反应的优先顺序给出。同一个反应不会重复出现。
Output
Sample Input
3 2 1
2 3 4
1 2
3 2
2 3
Sample Output
6

解题报告:
很容易想到:如果按操作建图,最后反应的顺序就是lca的深度,所以我们按照操作建好图,然后求出所有lca的深度,最后排个序就可以做了
那么问题来了:
如果先将3倒入1,再将2倒入1,1.2.3之间都可以反应,那么就不清楚反应顺序了,所以跟操作顺序也有很大关系,所以建图不能简单的直接从x连到y,需要新建一个节点,保证先输入的深度更大,那么就没有问题了.

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
const int N=800005,M=500005;
int head[N],num=0,to[N<<1],nxt[N<<1],n,a[N],m,Q,dep[N],fa[N],top[N];
int son[N],sz[N],ids=0,col[N];
void link(int x,int y){nxt[++num]=head[x];to[num]=y;head[x]=num;}
void dfs1(int x,int co){
    int u;sz[x]=1;col[x]=co;
    for(int i=head[x];i;i=nxt[i]){
        u=to[i];if(dep[u])continue;
        dep[u]=dep[x]+1;fa[u]=x;
        dfs1(u,co);sz[x]+=sz[u];
        if(sz[u]>sz[son[x]])son[x]=u;
    }
}
void dfs2(int x,int tp){
    top[x]=tp;
    if(son[x])dfs2(son[x],tp);
    for(int i=head[x];i;i=nxt[i])
        if(to[i]!=fa[x] && to[i]!=son[x])
            dfs2(to[i],to[i]);
}
struct node{
    int x,y,id,lca;
    bool operator <(const node &pr)const{
        if(dep[lca]!=dep[pr.lca])return dep[lca]>dep[pr.lca];
        return id<pr.id;
    }
}e[M];
int lca(int x,int y){
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        x=fa[top[x]];
    }
    if(dep[x]>dep[y])swap(x,y);
    return x;
}
int Fa[N],du[N];
void work()
{
    int x,y;
    scanf("%d%d%d",&n,&m,&Q);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]),Fa[i]=i;
    for(int i=1;i<=m;i++){
        scanf("%d%d",&x,&y);
        link(i+n,Fa[x]);link(Fa[y],i+n);
        link(Fa[x],i+n);link(i+n,Fa[y]);
        Fa[y]=i+n;
    }
    for(int i=n+m;i>=1;i--)
        if(!dep[i])dep[i]=1,dfs1(i,++ids),dfs2(i,i);
    for(int i=1;i<=Q;i++){
        scanf("%d%d",&e[i].x,&e[i].y);
        if(col[e[i].x]!=col[e[i].y])continue;
        e[i].lca=lca(e[i].x,e[i].y);
        e[i].id=i;
    }
    sort(e+1,e+Q+1);long long ans=0,res;
    for(int i=1;i<=Q;i++){
        x=e[i].x;y=e[i].y;
        if(col[x]!=col[y])continue;
        res=Min(a[x],a[y]);ans+=res<<1;
        a[x]-=res;a[y]-=res;
    }
    printf("%lld\n",ans);
}

int main()
{
    work();
    return 0;
}

转载于:https://www.cnblogs.com/Yuzao/p/7572817.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值