Direction Setting(2021 SCCPC F)
题目大意
有\(T\)组询问,每组询问给你\(n\)个点和\(m\)条边,每个点有一个边权\(a_i\),现在让你确定每条边的方向,定义\(d_i\)为第\(i\)个点的入度,使得\(D=\sum_{i=1}^n\max(0,d_i-a_i)\)最小。问\(D\)的最小值与每条边的方向,输出任一方案即可。\((2\leq n\leq300,1\leq m\leq300,0\leq a_i\leq10^4,\sum n\leq3\times10^3,\sum m\leq3\times10^3)\)
思路
我们考虑网络流,我们对起点和每条边都连一条流量为\(1\)的边,对每条边和它的端点连一条流量为\(1\)的边,再对每个点和汇点连一条流量为\(a_i\)的边,然后我们跑一遍\(Dinic\),最后的\(m-dinic\)就是最小的\(D\),然后对于每条边,它流向的某个端点的边中流量如果还是\(1\),那么说明这条边是往还有一个方向流的,反之亦然,于是我们就完成构造了。
代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1000005;
const int inf=1000000000;
int S,T;
struct EDGE
{
int v,flow,nxt;
}edge[MAXN<<2];
int head[MAXN],cur[MAXN],num=0;
int dep[MAXN],q[MAXN];
void add_edge(int u,int v,int w)
{
edge[num].v=v;
edge[num].flow=w;
edge[num].nxt=head[u];
head[u]=num++;
}
void Add_edge(int u,int v,int w)
{
add_edge(u,v,w);
add_edge(v,u,0);
}
bool bfs()
{
memset(dep,0,sizeof(dep));
dep[S]=1;
int l=0,r=1;
q[++l]=S;
while(l<=r)
{
int p=q[l++];
for(int i=head[p];~i;i=edge[i].nxt)
{
if(!dep[edge[i].v]&&edge[i].flow)
{
dep[edge[i].v]=dep[p]+1;
q[++r]=edge[i].v;
if(edge[i].v==T)return 1;
}
}
}
return dep[T];
}
int dfs(int now,int nowflow)
{
if(now==T)return nowflow;
int totflow=0;
for(int i=head[now];~i;i=edge[i].nxt)
{
if(dep[edge[i].v]==dep[now]+1&&edge[i].flow)
{
int canflow=dfs(edge[i].v,min(nowflow,edge[i].flow));
edge[i].flow-=canflow;
edge[i^1].flow+=canflow;
totflow+=canflow;
nowflow-=canflow;
if(nowflow<=0)break;
}
}
return totflow;
}
int dinic()
{
int ans=0;
while(bfs())
{
memcpy(cur,head,sizeof(head));
ans+=dfs(S,inf);
}
return ans;
}
void init()
{
memset(q,0,sizeof(q));
memset(cur,0,sizeof(cur));
memset(dep,0,sizeof(dep));
memset(head,-1,sizeof(head));
for(int i=0;i<=num;i++)edge[i].v=edge[i].flow=edge[i].nxt=0;
num=0;
}
int main()
{
int _;
scanf("%d",&_);
while(_--)
{
init();
int n,m;
scanf("%d%d",&n,&m);
S=601,T=602;
for(int i=301;i<=m+300;i++)Add_edge(S,i,1);
for(int i=1;i<=n;i++)
{
int a;
scanf("%d",&a);
Add_edge(i,T,a);
}
for(int i=301;i<=m+300;i++)
{
int u,v;
scanf("%d%d",&u,&v);
Add_edge(i,u,1);
Add_edge(i,v,1);
}
printf("%d\n",m-dinic());
for(int i=2*m+2*n+1;i<=2*m+2*n+4*m;i+=4)
{
if(edge[i].flow==1)printf("1");
else printf("0");
}
printf("\n");
}
return 0;
}
/*
2
4 5
0 1 1 5
1 2
1 3
2 3
3 2
4 4
3 2
0 0 2
1 3
3 2
*/

浙公网安备 33010602011771号