[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

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

Sample Input

2 2
2 2 2
2 5 5
2 2 5
5 5 5

Sample Output

2

Solution

首先暴力就直接\(O(n^2)\)建图然后最大匹配,这就不说了。

考虑如何减少状态,注意到值域很小,那么我们可以把质数全打出来,这样也只有四十多个。

把题意转化一下,至多一对互质就是至少两对不互质,那么我们可以暴力的两两枚举质数,然后对于每个质数对\((p_1,p_2)\),对于点对\((a,b)\)建一个点出来,设这个点为\(x\),然后考虑对于第\(i\)张卡牌,若是在第一组且\(p_1|a_i,p_2|b_i\),就把\(i\)\(x\)连容量为\(1\)的边,若第二组就\(x\)\(i\)连。

对于每对质数,对于\(a,b,c\)的三种组合都建出一个点,然后向上面那样连边。

对于第一组的每个点,连边\((s,i,1)\),对于第二组连边\((i,t,1)\),然后最大流就是答案。

正确性自己画一下就好了。

#include<bits/stdc++.h>
using namespace std;
 
void read(int &x) {
    x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
 
void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}

const int maxn = 3e5+10;
const int inf = 1e9;

int n,m,a[maxn],b[maxn],c[maxn],pri[300],vis[300],cnt,nd[200][200],pts;
int d[201][30],s,t;

void sieve() {
	for(int i=2;i<=200;i++) {
		if(!vis[i]) pri[++cnt]=i,d[i][d[i][0]=1]=i;
		for(int j=1,v;j<=cnt&&i*pri[j]<=200;j++) {
			vis[v=i*pri[j]]=1;
			memcpy(d[v],d[i],sizeof d[v]);
			if(i%pri[j]==0) break;
			d[v][++d[v][0]]=pri[j];
		}
	}
}

int tot=1,head[maxn],dis[maxn];
struct edge{int to,nxt,w;}e[maxn<<3];

void add(int u,int v,int w) {e[++tot]=(edge){v,head[u],w},head[u]=tot;}
void ins(int u,int v,int w) {add(u,v,w),add(v,u,0);}

void line(int u,int x,int y,int op,int k) {
	for(int i=1;i<=d[x][0];i++)
		for(int j=1,v;j<=d[y][0];j++) {
			v=nd[d[x][i]][d[y][j]]+k*46*46;
			if(!op) ins(u,v,1);
			else ins(v,u,1);
		}
}

int bfs() {
	memset(dis,-1,sizeof dis);
	queue<int > q;q.push(s);dis[s]=0;
	while(!q.empty()) {
		int x=q.front();q.pop();
		for(int i=head[x];i;i=e[i].nxt)
			if(e[i].w>0&&dis[e[i].to]==-1) {
				dis[e[i].to]=dis[x]+1;
				if(e[i].to==t) return 1;
				q.push(e[i].to);
			}
	}return 0;
}

int dfs(int x,int f) {
	if(x==t) return f;
	int used=0;
	for(int i=head[x];i;i=e[i].nxt)
		if(e[i].w>0&&dis[e[i].to]==dis[x]+1) {
			int D=dfs(e[i].to,min(f-used,e[i].w));
			if(D) e[i].w-=D,e[i^1].w+=D,used+=D;
			if(used==f) break;
		}
	if(!used) dis[x]=-1;
	return used;	
}

int max_flow() {
	int flow=0;
	while(bfs()) flow+=dfs(s,inf);
	return flow;
}

int main() {
	read(n),read(m);sieve();
	for(int i=1;i<=n;i++) read(a[i]),read(b[i]),read(c[i]);
	for(int i=n+1;i<=n+m;i++) read(a[i]),read(b[i]),read(c[i]);
	pts=n+m+3;s=n+m+1,t=s+1;
	for(int i=1;i<=cnt;i++)
		for(int j=1;j<=cnt;j++)
			nd[pri[i]][pri[j]]=++pts;
	for(int i=1;i<=n;i++) line(i,a[i],b[i],0,0),line(i,a[i],c[i],0,1),line(i,b[i],c[i],0,2);
	for(int i=n+1;i<=n+m;i++) line(i,a[i],b[i],1,0),line(i,a[i],c[i],1,1),line(i,b[i],c[i],1,2);
	for(int i=1;i<=n;i++) ins(s,i,1);
	for(int i=n+1;i<=n+m;i++) ins(i,t,1);
	write(max_flow());
	return 0;
}
posted @ 2019-03-10 09:16  Hyscere  阅读(301)  评论(0编辑  收藏  举报