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题(带花树)的主要内容,如果未能解决你的问题,请参考以下文章
XIX Russia Team Open, High School Programming Contest 解题报告
翻译‘BadRabbit’ Ransomware Burrows Into Russia, Ukraine
2019.1.7 Russia temperature control demo
Southern and Volga Russia Qualifier 2019-2020