BZOJ 3712: [PA2014]Fiolki 倍增+想法

3712: [PA2014]Fiolki

Time Limit: 30 Sec  Memory Limit: 128 MB
Submit: 437  Solved: 115
[Submit][Status][Discuss]

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],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]),按照反应的优先顺序给出。同一个反应不会重复出现。

Output

 

Sample Input

3 2 1
2 3 4
1 2
3 2
2 3

Sample Output

6

HINT

 

Source

鸣谢Jcvb

 

想法:

先看出树结构,然后分析:

一个反应最多进行一次,而且反应一定是在其LCA处反应。先不考虑中途其他反应,那么这个反应进行的时间是可以知道的。将这些反应按(反应时间,优先顺序)sort一下。模拟即可。

求反应时间就是求两个点最慢到LCA的时间,可以倍增。O(nlogn+klogn)

还有一种方法,按操作顺序,每操作一次建一个新容器用来装反应完的产物,例:x->y 这样: x->tmp y->tmp 然后y=tmp用来和后面的反应。

这样节点的深度与反应时间反相关,就可以不用倍增求,而用Tarjan求。从而O(n+k)

附上倍增代码

#include<cstdio>
#include<cmath>
#include<algorithm>

typedef long long ll;
const int len(200000),lem(500000),INF(0x7fffffff);
int n,m,k,g[len+10],a,b,logg;
int f[20][len+10],t[len+10],depth[len+10];
struct AB{int a,b,lca,id;}reaction[lem+10];
struct Node{int nd,nx;}bot[len+10];int tot,first[len+10];
ll precipitation,ans;

int max(int a,int b){return a>b?a:b;} 
int min(int a,int b){return a<b?a:b;}
bool cmp(AB A,AB B){return (A.lca<B.lca)||(A.lca==B.lca&&A.id<B.id);}
void add(int a,int b){bot[++tot]=(Node){b,first[a]};first[a]=tot;}
void swap(int &a,int &b){if(a==b)return;a^=b;b^=a;a^=b;}
template <class T>void read(T &x)
{
    x=0;int f=0;char c=getchar();
    while((c<'0'||c>'9')&&c!='-')c=getchar(); if(c=='-')f=1,c=getchar();
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    x=f?-x:x;
}

void DFS(int x)
{
    depth[x]++;
    for(int v=first[x];v;v=bot[v].nx)
    depth[bot[v].nd]=depth[x],DFS(bot[v].nd);
}
int lca(int a,int b)
{
    if(depth[a]<depth[b])swap(a,b);
    for(int k=depth[a]-depth[b]-1,j=0;k>0;k>>=1,j++)
    if(k&1)a=f[j][a];
    if(f[0][a]==b)return t[a];
    if(depth[a]>depth[b]) a=f[0][a];
    for(int j=logg;f[0][a]!=f[0][b];j--)
    if(f[j][a]!=f[j][b])a=f[j][a],b=f[j][b];
    return max(t[a],t[b]);
}
int main()
{
//    freopen("C.in","r",stdin);
//    freopen("C.out","w",stdout);
    read(n),read(m),read(k);
    for(int i=1;i<=n;i++)read(g[i]);
    for(int i=1;i<=m;i++)
    {
        read(a),read(b);
        f[0][a]=b; t[a]=i;    add(b,a);
    }
    for(int i=1;i<=n;i++)if(!f[0][i])f[0][i]=n+1,add(n+1,i),t[i]=INF;
    DFS(n+1);
    logg=log2(n);
    for(int j=1;j<=logg;j++)
       for(int i=1;i<=n;i++)
       f[j][i]=f[j-1][ f[j-1][i] ];
    for(int i=1;i<=k;i++)
    {
        read(a),read(b);
        reaction[i].a=a; 
        reaction[i].b=b;
        reaction[i].lca=lca(a,b);
//        if(!reaction[i].lca)fprintf(stderr,"%d\n",i);
        reaction[i].id=i;
    }
    std::sort(reaction+1,reaction+1+k,cmp);
    for(int i=1;i<=k;i++)
    if(reaction[i].lca!=INF) 
    {
        precipitation=min(g[reaction[i].a],g[reaction[i].b]);
        ans+=precipitation;
        g[reaction[i].a]-=precipitation;
        g[reaction[i].b]-=precipitation;
    }else break; 
    printf("%lld",ans<<1);
    return 0;
}
View Code

 

posted @ 2017-04-13 21:00  Oncle_Ha  阅读(246)  评论(0编辑  收藏  举报