BUAA 415 合并果子(并查集+二叉堆)

题目链接:http://acm.buaa.edu.cn/problem/415/

题意:n个数,每次合并两个数,代价为这两个数的和。已知在第i次合并前有一些修改。每次合并时合并两个最小的数字。问最后的代价为多少?

思路:维护一个小根堆,记录每一个数在堆中的编号。




struct node
{
    int id;
    i64 cnt;
};

struct Event
{
    int id,det;

    Event(){}
    Event(int _id,int _det)
    {
        id=_id;
        det=_det;
    }
};


const i64 INF=1e18;
const int MAX=10005;
node T[MAX];
int n,A,B,p[MAX];
vector<Event> V[MAX];

int C;

int Set[MAX];

void setInit(int n)
{
    int i;
    FOR1(i,n) Set[i]=i;
}

int setFind(int x)
{
    if(Set[x]!=x) Set[x]=setFind(Set[x]);
    return Set[x];
}



void down(int x)
{
    int L,R,t;
    node temp;
    while(1)
    {
        L=x*2;
        R=x*2+1;
        if(L<=n&&(T[L].cnt<T[x].cnt||T[L].cnt==T[x].cnt&&T[L].id<T[x].id))
        {
            t=L;
        }
        else t=x;
        if(R<=n&&(T[R].cnt<T[t].cnt||T[R].cnt==T[t].cnt&&T[R].id<T[t].id))
        {
            t=R;
        }
        if(t==x) return;
        temp=T[x];
        T[x]=T[t];
        T[t]=temp;
        p[T[t].id]=t;
        p[T[x].id]=x;
        x=t;
    }
}

void up(int x)
{
    int pre,t;
    node temp;
    while(1)
    {
        pre=x/2;
        if(pre>=1&&(T[pre].cnt>T[x].cnt||T[pre].cnt==T[x].cnt&&T[pre].id>T[x].id))
        {
            t=pre;
        }
        else t=x;
        if(t==x) return;
        temp=T[t];
        T[t]=T[x];
        T[x]=temp;
        p[T[t].id]=t;
        p[T[x].id]=x;
        x=t;
    }
}

i64 deal(int x)
{
    i64 i,ans=0,t;
    node temp;
    for(i=0;i<SZ(V[x]);i++)
    {
        t=setFind(V[x][i].id);
        t=p[t];
        T[t].cnt+=V[x][i].det;
        if(V[x][i].det<0) up(t);
        else down(t);
    }

    int preid=T[1].id;
    ans+=T[1].cnt;
    T[1]=T[n--];
    down(1);
    ans+=T[1].cnt;
    T[1].cnt=ans;

    if(preid>T[1].id) Set[preid]=T[1].id;
    else
    {
        Set[T[1].id]=preid;
        p[preid]=1;
        T[1].id=preid;
    }

    down(1);
    return ans;
}



int main()
{
    for(scanf("%d",&C);C--;)
    {
        RD(n,A,B);
        int i,j,x,y,z;
        FOR1(i,n)
        {
            RD(T[i].cnt);
            p[i]=T[i].id=i;
        }
        DOW1(i,n/2) down(i);
        FOR1(i,n) CLR(V[i]);
        FOR1(i,A)
        {
            RD(x,y,z);
            V[x].pb(Event(y,-z));
        }
        FOR1(i,B)
        {
            RD(x,y,z);
            V[x].pb(Event(y,z));
        }
        setInit(n);
        i64 ans=0,tempN=n-1;
        FOR1(i,tempN) ans+=deal(i);
        PR(ans);
    }
    return 0;
}

  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值