【PA2014】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,k( 0<=m<n<=200000,0<=k<=500000 ),分别表示药瓶的个数(即物质的种数),操作步数,可以发生的反应数量。

第二行有n个整数g[1],g[2],…,g[n](1<=g[i]<= 109 ),表示初始时每个瓶内物质的质量。

接下来m行,每行两个整数 a[i],b[i](1<=a[i],b[i]<=n,a[i]b[i]) ,表示第i个步骤。保证a[i]在以后的步骤中不再出现。

接下来k行,每行是一对可以发生反应的物质 c[i],d[i](1<=c[i],d[i]<=n,c[i]d[i]) ,按照反应的优先顺序给出。同一个反应不会重复出现。

输出

总共产生多少沉淀

思路

暴力模拟的话……需要考虑的东西太多了:每个瓶子的药剂的种类、质量,以及何时反应,与什么反应……存都存不下……

先分析一下影响反应发生先后顺序的因素:
1.反应发生的时间
2.反应发生的优先级
其中1优先于2

有没有什么办法只关心反应发生的时间和优先级,又能求出答案?

如果从某种药剂出发,要么是倒入其他瓶中,要么是其他药剂倒入它所在的瓶子中,发生某某反应,是一个线状的过程。因此我们可以想到将每个瓶子抽象成一个点,把瓶子A倒入瓶子B,就由A向B连边。由于A、B混合后的B不同于原来的B,不妨将A和B连向新的点C,表示A、B混合后的B。

这样经过一系列操作后,会得到一颗或多棵树(把它倒过来看,除根以外每个点的入度皆为1)。对于药剂i和j,它们反应的场所就是对应树上的LCA。我们记录下每组会发生反应的药剂的LCA的深度,那么深度越大的就越先发生。

预处理出每组药剂的LCA,将它们的深度为第一关键字,优先度为第二关键字,排序一遍,就解决了反应发生先后顺序的问题,模拟一遍即可。

时间复杂度为O( klogn )。

代码

#include 
#include 
#include 

using namespace std;
typedef long long ll;

const int MAXN=400010;
const int MAXM=500010;

int n,m,k,g[MAXN],a[MAXN],b[MAXN],c[MAXM],d[MAXM],Fa[MAXN],pn,cnt,tot;
int fa[MAXN][20],dep[MAXN],dfn;
bool vis[MAXN];
ll ans;

int head[MAXN];
struct edge{int to,nxt;}e[MAXM<<1];
struct reaction{
    int x,y,d,t;
    reaction(){}
    reaction(int t1,int t2,int t3,int t4){
        x=t1; y=t2; d=t3; t=t4;
    }
}rea[MAXM];

bool cmp(reaction t1,reaction t2){
    return t1.d>t2.d || t1.d==t2.d && t1.tint getint(){
    int v=0; char ch;
    while(!isdigit(ch=getchar())); v=ch-48;
    while(isdigit(ch=getchar())) v=v*10+ch-48; return v;
}

void insert(int u,int v){
    e[++cnt].to=v; e[cnt].nxt=head[u]; head[u]=cnt;
}

int find(int x){
    return Fa[x]==x?x:Fa[x]=find(Fa[x]);
}

void dfs(int x){
    vis[x]=true;
    for(int i=1;(1<1]][i-1];
    for(int i=head[x];i;i=e[i].nxt){
        dep[e[i].to]=dep[x]+1;
        fa[e[i].to][0]=x;
        dfs(e[i].to);
    }
}

int lca(int x,int y){
    if(dep[x]int det=dep[x]-dep[y];
    for(int i=0;(1<if((1<if(x==y) return x;
    for(int i=18;~i;i--)
        if(fa[x][i]!=fa[y][i])
            x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}

int main(){
    n=getint(); m=getint(); k=getint(); pn=n;
    for(int i=1;i<=n;i++) g[i]=getint(),Fa[i]=i;
    for(int i=1;i<=m;i++) a[i]=getint(),b[i]=getint();
    for(int i=1;i<=k;i++) c[i]=getint(),d[i]=getint();
    for(int i=1;i<=m;i++){
        ++pn;
        int f1=find(a[i]),f2=find(b[i]);
        insert(pn,f1);
        insert(pn,f2);
        Fa[f1]=Fa[f2]=Fa[pn]=pn;
    }
    for(int i=pn;i;i--)
        if(!vis[i]) dfn++,dfs(i);
    for(int i=1;i<=k;i++){
        if(find(c[i])==find(d[i]))
            rea[++tot]=reaction(c[i],d[i],dep[lca(c[i],d[i])],i);
    }
    sort(rea+1,rea+1+tot,cmp);
    for(int i=1;i<=tot;i++){
        int x=rea[i].x,y=rea[i].y,det=min(g[x],g[y]);
        g[x]-=det; g[y]-=det; ans+=det<<1;
    }
    printf("%lld\n",ans);
    return 0;
}

你可能感兴趣的:(题解)