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题(带花树)的主要内容,如果未能解决你的问题,请参考以下文章