bzoj 3712 [PA2014]Fiolki(LCA)

           这题写的好蛋疼,被一个bug卡了半天。。。这题主要就是要确定所有反应发生的顺序,因为一种反应只能发生一次。观察混合的过程,这其实可以看做是建一颗树,混合以后相当于建一个新结点,那么就可以用这个结点表示混合后的状态……那么,很容易发现,一个反应如果发生,那么就是在两个药水的lca处发生。接下来,相对顺序其实不重要,只要保证一个反应的子树中的反应都发生了就行了,因此,把所有的反应按其lca的深度排序,然后逐个计算就行了。刚开始虽然想到了会形成森林,但是tarjan求lca的时候没写好,导致lca会出现问题,调了半天,fuck……


代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<set>
#include<cmath>
#include<vector>
#include<bitset>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-6
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn = 500000 + 10;
vector<int>G[maxn];
vector<pair<int,int> >querys[maxn];
int pa[maxn],d[maxn],anc[maxn],w[maxn],tot;
pair<int,int>op[maxn],np[maxn];
int vis[maxn],flag[maxn],cnt;
int Find(int x)
{
    return x == pa[x] ? x : pa[x] = Find(pa[x]);
}
void tarjan(int u)
{
    pa[u] = u;
    vis[u] = cnt;
    for(int i = 0;i < (int)querys[u].size();++i)
    {
        int v = querys[u][i].first;
        int x = querys[u][i].second;
        if(vis[v] == cnt)
        {
            np[tot++] = make_pair(d[Find(v)],x);
        }
    }
    for(int i = 0;i < (int)G[u].size();++i)
    {
        int v = G[u][i];
        d[v] = d[u] - 1;
        tarjan(v);
        pa[v] = u;
    }
}
int main()
{
//    freopen("in.txt","r",stdin);
//    freopen("out.txt","w",stdout);
    int n,m,k;
    scanf("%d%d%d",&n,&m,&k);
    memset(anc,0xff,sizeof(anc));
    for(int i = 1; i <= n;++i)
        scanf("%d",&w[i]);
    for(int i = 1;i <= n + m;++i)
        pa[i] = i;
    int u,v;
    int N = n;
    for(int i = 1;i <= m;++i)
    {
        scanf("%d%d",&u,&v);
        u = Find(u);
        v = Find(v);
        N++;
        pa[u] = pa[v] = N;
        G[N].push_back(v);
        G[N].push_back(u);
        flag[u] = flag[v] = true;
    }
    for(int i = 1;i <= k;++i)
    {
        scanf("%d%d",&u,&v);
        op[i] = make_pair(u,v);
        querys[u].push_back(make_pair(v,i));
        querys[v].push_back(make_pair(u,i));
    }
    tot = cnt = 0;
    for(int i = 1; i <= N;++i)
        if(!flag[i])
        {
            cnt++;
            tarjan(i);
        }
    sort(np,np + tot);
    ll ans = 0;
    for(int i = 0;i < tot;++i)
    {
        int x = np[i].second;
        u = op[x].first;
        v = op[x].second;
        if(w[u] && w[v])
        {
            int y = min(w[u],w[v]);
            ans += y*2LL;
            w[u] -= y;
            w[v] -= y;
        }
    }
    cout<<ans<<endl;
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值