2020-2021 Russia Team Open, High School Programming Contest G题(带花树)

Posted 吃花椒的妙酱

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2020-2021 Russia Team Open, High School Programming Contest G题(带花树)相关的知识,希望对你有一定的参考价值。

题目大意:n种菜,每种菜有ai份,现在给两两菜式一起煮熟所花的时间,求所有菜煮熟的最小时间(n<=10 , ai <=50)

思路:这么小的数据范围肯定网络流了

考虑建图,看到时间、一次煮两份才很容易想到最小费用,然后就不会建图了,我们假设容量为菜的份数,花费为时间,我们很难将两份菜同时煮转化到图上

换算法,一考虑拆点,拆成a1+a2+a3+……an个点,能匹配的点连边,这样建图后存在奇环,km显然不行,然后就是一般图最大权匹配的板子题了(bushi)

这里求的最小花费,将所有边取负数加上一个delta使边为正,跑一下带花树,最后结果减去 匹配边数*delta,再取相反数就是最小花费了

核心就是拆点建图,太巧妙了

#include <bits/stdc++.h>
#define N 810
using namespace std;
typedef long long ll;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
struct edge{int u,v,w;}mps[N][N];
int n,m,mat[N],pre[N],bl[N],fa[N],tim;ll totw=0;
int sign[N],lab[N],slacku[N],blofm[N][N],tot,mx;
vector <int> leaves[N];
int q[N],hd;
inline int calc_e(const edge &e)
{
    return lab[e.u]+lab[e.v]-mps[e.u][e.v].w*2;
}
inline void updata(int u,int x)
{
    if(!slacku[x]||calc_e(mps[u][x])<calc_e(mps[slacku[x]][x]))
        slacku[x]=u;
}
inline void calc_slack(int x)
{
    slacku[x]=0;
    for(int i=1;i<=n;i++)
        if(mps[i][x].w>0&&fa[i]!=x&&sign[fa[i]]==0)
            updata(i,x);
}
inline void q_push(int x)
{
    if(x<=n) q[++hd]=x;
    else for(int i=0;i<(int)leaves[x].size();i++)
        q_push(leaves[x][i]);
}
inline int get_lca(int x, int y)
{
    if(tim=100000000)
        memset(bl,0,sizeof bl),tim=0;
    for(++tim;x||y;swap(x,y)) if(x)
    {
        if(bl[x]==tim) return x;
        bl[x]=tim; x=fa[mat[x]];
        if(x) x=fa[pre[x]];
    }
    return 0;
}
inline void set_fa(int x,int y)
{
    fa[x]=y; if(x>n)
    for(int i=0;i<(int)leaves[x].size();i++)
        set_fa(leaves[x][i],y);
}
inline void set_mat(int x,int y)
{
    mat[x]=mps[x][y].v;
    if(x<=n) return ;
    int xr=blofm[x][mps[x][y].u];
    int pr=find(leaves[x].begin(),leaves[x].end(),xr)-leaves[x].begin();
    if(pr%2==1)
        reverse(leaves[x].begin()+1, leaves[x].end()),
        pr=(int)leaves[x].size()-pr;
    for(int i=0;i<pr;i++)
        set_mat(leaves[x][i],leaves[x][i^1]);
    set_mat(xr,y);
    rotate(leaves[x].begin(),leaves[x].begin()+pr,leaves[x].end());
}
inline void blossom_blooms(int x)
{
    for(int i=0;i<(int)leaves[x].size();i++)
    {
        if(leaves[x][i]>n&&!lab[leaves[x][i]])
            blossom_blooms(leaves[x][i]);
        else set_fa(leaves[x][i],leaves[x][i]);
    }
    fa[x]=0;
}
inline void blossom_make(int u,int lca,int v)
{
    int x=n+1; while(x<=tot&&fa[x]) x++;
    if(x>tot) tot++;
    lab[x]=sign[x]=0;
    mat[x]=mat[lca]; leaves[x].clear();
    leaves[x].push_back(lca);
    for(int i=u;i!=lca;i=fa[pre[fa[mat[i]]]])
        leaves[x].push_back(i),leaves[x].push_back(fa[mat[i]]),q_push(fa[mat[i]]);
    reverse(leaves[x].begin()+1, leaves[x].end());
    for(int i=v;i!=lca;i=fa[pre[fa[mat[i]]]])
        leaves[x].push_back(i),leaves[x].push_back(fa[mat[i]]),q_push(fa[mat[i]]);
    set_fa(x,x);
    for(int i=1;i<=tot;i++)
        mps[x][i].w=mps[i][x].w=0, blofm[x][i]=0;
    for(int i=0;i<(int)leaves[x].size();i++)
    {
        int xs=leaves[x][i];
        for(int j=1;j<=tot;j++)
            if(!mps[x][j].w||calc_e(mps[xs][j])<calc_e(mps[x][j]))
                mps[x][j]=mps[xs][j],mps[j][x]=mps[j][xs];
        for(int j=1;j<=tot;j++)
            if(blofm[xs][j]) blofm[x][j]=xs;
    }
    calc_slack(x);
}
inline void link(int x,int y)
{
    while(1)
    {
        int xx=fa[mat[x]];
        set_mat(x,y);
        if(!xx) return ;
        set_mat(xx,fa[pre[xx]]);
        x=fa[pre[xx]]; y=xx;
    }
}
inline int deal_edge(const edge &e)
{
    int u=fa[e.u],v=fa[e.v];
    if(sign[v]==-1) //unsigned
    {
        pre[v]=e.u; // cause we bfs all vertices tegother,we dont' need to discuss two situation
        sign[v]=1; sign[fa[mat[v]]]=0;
        slacku[v]=slacku[fa[mat[v]]]=0;
        q_push(fa[mat[v]]);
    }
    else if(!sign[v]) //S signed vertex
    {
        int lca=get_lca(u,v);
        if(!lca)
        {
            link(u,v); link(v,u); //connected! new argument.
            for(int i=n+1;i<=tot;i++)
                if(fa[i]==i&&lab[i]==0)
                    blossom_blooms(i); // flower may not be a flower any more so we blossom blooms!
            return 1;
        }
        else blossom_make(u,lca,v); // form a new flower!
    }
    return 0;
}
inline void blossom_bloom_1(int x)
{

    for(int i=0;i<(int)leaves[x].size();i++)
        set_fa(leaves[x][i],leaves[x][i]);
    int xr=blofm[x][mps[x][pre[x]].u];
    int pr=find(leaves[x].begin(), leaves[x].end(),xr)-leaves[x].begin();
    if(pr%2==1)
        reverse(leaves[x].begin()+1, leaves[x].end()),
        pr=(int)leaves[x].size()-pr;
    for(int i=0;i<pr;i+=2)
    {
        int u=leaves[x][i],v=leaves[x][i+1];
        pre[u]=mps[v][u].u;
        sign[u]=1; sign[v]=0;
        slacku[u]=0; calc_slack(v); q_push(v);
    }
    sign[xr]=1; pre[xr]=pre[x];
    for(int i=pr+1;i<(int)leaves[x].size();i++)
    {
        int u=leaves[x][i];
        sign[u]=-1; calc_slack(u);
    }
    fa[x]=0;
}
inline int match()
{
    for(int i=1;i<=tot;i++) slacku[i]=0,sign[i]=-1;
    hd=0; for(int i=1;i<=tot;i++)
        if(fa[i]==i&&!mat[i])
            slacku[i]=pre[i]=sign[i]=0,q_push(i);
    if(!hd) return 0;
    while(1)
    {
        for(int i=1;i<=hd;i++)
        {
            int lx=q[i]; for(int j=1;j<=n;j++)
                if(mps[lx][j].w>0&&fa[lx]!=fa[j])
                {
                    if(!calc_e(mps[lx][j]))
                    {
                        if(deal_edge(mps[lx][j]))
                            return 1;
                    }
                    else if(sign[fa[j]]!=1) updata(lx,fa[j]);
                }
        }
        int d=0x3fffffff;
        for(int i=1;i<=n;i++) if(!sign[fa[i]])
            d=min(d,lab[i]);
        for(int i=n+1;i<=tot;i++)
            if(fa[i]==i&&sign[i]==1)
                d=min(lab[i]/2,d);
        for(int i=1;i<=tot;i++) if(fa[i]==i&&slacku[i])
        {
            if(sign[i]==-1) d=min(calc_e(mps[slacku[i]][i]),d);
            else if(sign[i]==0) d=min(calc_e(mps[slacku[i]][i])/2,d);
        }
        for(int i=1;i<=n;i++)
            if(sign[fa[i]]==0) lab[i]-=d;
            else if (sign[fa[i]]==1) lab[i]+=d;
        for(int i=n+1;i<=tot;i++)
            if(fa[i]==i)
            {
                if(sign[i]==0) lab[i]+=d*2;
                else if(sign[i]==1) lab[i]-=d*2;
            }
        hd=0;
        for(int i=1;i<=n;i++) if(!lab[i]) return 0; //all vetices matched,single vetices's label = 0
        for(int i=1;i<=tot;i++)
            if(fa[i]==i&&slacku[i]&&fa[slacku[i]]!=i&&calc_e(mps[slacku[i]][i])==0)
                /*new edge*/ if(deal_edge(mps[slacku[i]][i])) return 1;
        for(int i=n+1;i<=tot;i++)
            if(fa[i]==i&&sign[i]==1&&!lab[i])
                blossom_bloom_1(i);
    }
    return 0;
}
inline void solve()
{
    for(int i=1;i<=n;i++) mat[i]=0;
    tot=n; hd=totw=0;
    for(int i=0;i<=n;i++) fa[i]=i,leaves[i].clear();
    for(int i=1;i<=n;i++) for(int j=1;j<=n;j++)
        blofm[i][j]=(i==j? i:0);
    for(int i =1;i<=n;i++) lab[i]=mx; //init label
    while(match());
    for(int i=1;i<=n;i++) if(mat[i]&&mat[i]<i)
        totw+=mps[i][mat[i]].w;
}
int k;
int a[15];
int mp[50][50];
int belong[510];
vector <int > v[510];
int main()
{
//    freopen("data.txt","r",stdin);
    cin>>k;
    for(int i=1 ;i<=k ;i++) cin>>a[i],n += a[i];
    for(int i=1 ;i<=k ;i++)
    {
        for(int j=1 ;j<=k ;j++)
        {
            cin>>mp[i][j];
        }
    }
    if( n&1 )
    {
        cout<<-1<<endl;
        return 0;
    }
    int delta = 105;//
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            mps[i][j]=(edge){i,j,0};
    int cnt=0;//拆点后的编号
    for(int i=1 ;i<=k ;i++)
    {
        while(a[i])
        {
            v[i].push_back(++cnt);
            belong[cnt] = i;
            a[i]--;
        }
    }
    //建图
    for(int i=1 ;i<=n ;i++)
    {
        for(int j=i+1 ;j<=n ;j++)
        {
            int id1 = belong[i];
            int id2 = belong[j];
            mps[i][j].w=mps[j][i].w=delta-mp[id1][id2]; mx=max(mx,mp[id1][id2]);
        }
    }
    solve(); printf("%lld\\n",ma*n/2 - totw);

}

以上是关于2020-2021 Russia Team Open, High School Programming Contest G题(带花树)的主要内容,如果未能解决你的问题,请参考以下文章