20200610 SCOI模拟T1(网络流)

T1 P3973 [TJOI2015]线性代数

思路:
矩阵 A 为一个 n*m 的矩阵
矩阵 A 的转置 A T A^T AT 即为一个 m*n 的矩阵,其中 A i , j T = A j , i A^T_{i,j}=A_{j,i} Ai,jT=Aj,i


( A ∗ B − C ) ∗ A T A ∗ B ∗ A T − C ∗ A T (A*B-C)*A^T\\ A*B*A^T-C*A^T (ABC)ATABATCAT
对于 B 中的一个数 B i , j B_{i,j} Bi,j,它的贡献为 B i , j ∗ A i ∗ A j B_{i,j}*A_{i}*A_{j} Bi,jAiAj,当 A i A_{i} Ai A j A_{j} Aj 都为 1 时, B i , j B_{i,j} Bi,j 有贡献
对于 C 中的一个数 C i , j C_{i,j} Ci,j,它的贡献为 C i ∗ A i C_{i}*A_{i} CiAi,当 A i A_{i} Ai1 时, C i C_{i} Ci 有贡献

于是考虑最小割
先全选 B i , j B_{i,j} Bi,j,然后割去最少的 B i , j B_{i,j} Bi,j C i C_{i} Ci
两点间连 INF 边表示两点不可分割
于是源点向 B i , j B_{i,j} Bi,j 连边,流量为 B i , j B_{i,j} Bi,j
B i , j B_{i,j} Bi,j C i C_{i} Ci C j C_{j} Cj 连边,流量为 INF
C i C_{i} Ci 向汇点连边,流量为 C j C_{j} Cj

代码:

#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define S 0
#define T n*(n+1)+1

inline int in{
	int s=0,f=1;char x;
	for(x=getchar();x<'0'||x>'9';x=getchar())	if(x=='-')	f=-1;
	for( ;x>='0'&&x<='9';x=getchar())	s=(s*10)+(x&15);
	return f==1?s:-s;
}

const int A=1e3+5;
const int B=2e6+5;
const int INF=(1<<30);
int n;
int a[A][A],b[A];
int head[B],tot_road=1;
struct Road{
	int nex,to,w;
}road[2*B];
inline void ljb(int x,int y,int w){
	road[++tot_road]={head[x],y,w};head[x]=tot_road;
}
inline int d1(int x,int y){
	return (x-1)*n+y;
}
inline int d2(int x){
	return n*n+x;
}

inline void scan(){
	n=in;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			a[i][j]=in;
	for(int i=1;i<=n;i++)	b[i]=in;
	return;
}

inline void build(){
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++){
			ljb(S,d1(i,j),a[i][j]),ljb(d1(i,j),S,0);
			if(i==j){
				ljb(d1(i,j),d2(i),INF),ljb(d2(i),d1(i,j),0);
			}
			else{
				ljb(d1(i,j),d2(i),INF),ljb(d2(i),d1(i,j),0);
				ljb(d1(i,j),d2(j),INF),ljb(d2(j),d1(i,j),0);
			}
		}
	for(int i=1;i<=n;i++)
		ljb(d2(i),T,b[i]),ljb(T,d2(i),0);
	return;
}

int maxflow=0;
int dep[B],sum[B];

inline void BFS(){
	memset(dep,-1,sizeof(dep));
	memset(sum,0,sizeof(sum));
	queue <int> q;
	dep[T]=0,sum[dep[T]]++;
	q.push(T);
	while(!q.empty()){
		int x=q.front();q.pop();
		for(int y=head[x];y;y=road[y].nex){
			int z=road[y].to;
			if(dep[z]!=-1)	continue;
			dep[z]=dep[x]+1,sum[dep[z]]++;
			q.push(z);
		}
	}
	return;
}

inline int DFS(int x,int flow){
	if(x==T){
		maxflow+=flow;
		return flow;
	}
	int used=0;
	for(int y=head[x];y;y=road[y].nex){
		int z=road[y].to,w=road[y].w;
		if(w&&dep[z]==dep[x]-1){
			int after=DFS(z,min(w,flow-used));
			if(after){
				used+=after;
				road[y].w-=after;
				road[y^1].w+=after;
			}
		}
		if(used==flow)	return used;
	}
	if(!--sum[dep[x]])	dep[T]=T+1;
	sum[++dep[x]]++;
	return used;
}

inline void ISAP(){
	maxflow=0;
	BFS();
	while(dep[T]<=T)	DFS(S,INF);
	return;
}

inline void print(){
	int ans=0;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			ans+=a[i][j];
	printf("%d\n",ans-maxflow);
	return;
}

signed main(){
	scan();
	build();
	ISAP();
	print();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值