【BZOJ4205】卡牌配对(网络流)

Description

现在有一种卡牌游戏,每张卡牌上有三个属性值:A,B,C。把卡牌分为X,Y两类,分别有n1,n2张。

两张卡牌能够配对,当且仅当,存在至多一项属性值使得两张卡牌该项属性值互质,且两张卡牌类别不同。

比如一张X类卡牌属性值分别是225,233,101,一张Y类卡牌属性值分别为115,466,99。那么这两张牌是可以配对的,因为只有101和99一组属性互质。

游戏的目的是最大化匹配上的卡牌组数,当然每张卡牌只能用一次。

Input

数据第一行两个数n1,n2,空格分割。

接下来n1行,每行3个数,依次表示每张X类卡牌的3项属性值。

接下来n2行,每行3个数,依次表示每张Y类卡牌的3项属性值。

Output

输出一个整数:最多能够匹配的数目。

第一眼:二分图水逼题!

第二眼:哦, n ≤ 30000 nleq 30000 n30000 啊,当我没说……

考场上没能想出来,考完后看到了分三类的神仙做法:

因为题目要求至多一种属性值互质,转化一下就是至少两种属性值不互质,也就是说只要有两种属性值不互质就好了。

那么我们就分三种情况讨论:两张卡牌的 A B AB AB 两种属性值不互质、两张卡牌的 A C AC AC 两种属性值不互质、两张卡牌的 B C BC BC 两种属性值不互质。

然后看到值域是 [ 1 , 200 ] [1,200] [1,200],就考虑用值域建图:先把 [ 1 , 200 ] [1,200] [1,200] 的所有质数预处理出来,有 46 46 46 个。然后两张卡牌的某种属性值不互质的条件就是它们有至少一个相同的质因数。假设现在讨论的情况是 A B AB AB,那么我们就可以枚举 X 类的每一张卡牌 i i i,然后枚举 i . A i.A i.A 的质因数 p r i m e j prime_j primej,枚举 i . B i.B i.B 的质因数 p r i m e k prime_k primek,让左边的 i i i ( j , k ) (j,k) (j,k) 连边。然后同理枚举 Y 类的卡牌 i i i,只不过是 ( j , k ) (j,k) (j,k) 向右边的 i i i 连边。

最后 S S S 向左边的每个 i i i 连边,右边的每个 i i i T T T 连边,跑一边最大流。

图大概长这样:

在这里插入图片描述
代码:

#include<bits/stdc++.h>

#define K 50
#define N 30010
#define INF 0x7fffffff
#define get(x,y) ((x-1)*tot+y)

using namespace std;

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<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}

int n,m,s,t,x[N][4],y[N][4];
int tot,prime[K];
int f1[]={1,1,2},f2[]={2,3,3};
int cnt=1,head[N*2+K*K*3],cur[N*2+K*K*3],to[N*112],nxt[N*112],c[N*112];
int num[N*2+K*K*3];
bool notprime[210];

queue<int>q;
vector<int>factor[210];

void init()
{
	for(int i=2;i<=200;i++)
	{
		if(!notprime[i]) prime[++tot]=i;
		for(int j=1;j<=tot&&i*prime[j]<=200;j++)
		{
			notprime[i*prime[j]]=true;
			if(!(i%prime[j])) break;
		}
	}
	for(int i=2;i<=200;i++)
	{
		for(int j=1;j<=tot;j++)
		{
			if(prime[j]>i) break;
			if(!(i%prime[j]))
				factor[i].push_back(j);
		}
	}
}

void adde(int u,int v,int ci)
{
	to[++cnt]=v;
	c[cnt]=ci;
	nxt[cnt]=head[u];
	head[u]=cnt;
	
	to[++cnt]=u;
	c[cnt]=0;
	nxt[cnt]=head[v];
	head[v]=cnt;
}

bool bfs()
{
	memcpy(cur,head,sizeof(cur));
	memset(num,-1,sizeof(num));
	q.push(s);
	num[s]=0;
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		for(int i=head[u];i;i=nxt[i])
		{
			int v=to[i];
			if(c[i]&&num[v]==-1)
			{
				num[v]=num[u]+1;
				q.push(v);
			}
		}
	}
	return num[t]!=-1;
}

int dfs(int u,int minflow)
{
	if(!minflow||u==t) return minflow;
	int preflow=0,nowflow;
	for(int i=cur[u];i;i=nxt[i])
	{
		cur[u]=i;
		int v=to[i];
		if(num[v]==num[u]+1&&(nowflow=dfs(v,min(minflow-preflow,c[i]))))
		{
			preflow+=nowflow;
			c[i]-=nowflow;
			c[i^1]+=nowflow;
			if(!(minflow-preflow)) break;
		}
	}
	return preflow;
}

int dinic()
{
	int maxflow=0;
	while(bfs())
		maxflow+=dfs(s,INF);
	return maxflow;
}

int main()
{
	init();
	n=read(),m=read();
	s=1,t=1+n+tot*tot*3+m+1;
	for(int i=1;i<=n;i++)
	{
		adde(s,1+i,1);
		for(int j=1;j<=3;j++) 
			x[i][j]=read();
	}
	for(int i=1;i<=m;i++)
	{
		adde(1+n+tot*tot*3+i,t,1);
		for(int j=1;j<=3;j++) 
			y[i][j]=read();
	}
	for(int t=0;t<3;t++)
	{
		int a=f1[t],b=f2[t];
		for(int i=1;i<=n;i++)
			for(int j=0,sizej=factor[x[i][a]].size();j<sizej;j++)
				for(int k=0,sizek=factor[x[i][b]].size();k<sizek;k++)
					adde(1+i,1+n+t*tot*tot+get(factor[x[i][a]][j],factor[x[i][b]][k]),1);				
		for(int i=1;i<=m;i++)
			for(int j=0,sizej=factor[y[i][a]].size();j<sizej;j++)
				for(int k=0,sizek=factor[y[i][b]].size();k<sizek;k++)
					adde(1+n+t*tot*tot+get(factor[y[i][a]][j],factor[y[i][b]][k]),1+n+tot*tot*3+i,1);	
	}
	printf("%d
",dinic());
	return 0;
}
原文地址:https://www.cnblogs.com/ez-lcw/p/14448661.html