卡牌配对
【问题描述】
现在有一种卡牌游戏,每张卡牌上有三个属性值:A,B,C。把卡牌分为X,Y两类,分别有n1,n2张。
两张卡牌能够配对,当且仅当,存在至多一项属性值使得两张卡牌该项属性值互质,且两张卡牌类别不同。
比如一张X类卡牌属性值分别是225,233,101,一张Y类卡牌属性值分别为115,466,99。那么这两张牌是可以配对的,因为只有101和99一组属性互质。
游戏的目的是最大化匹配上的卡牌组数,当然每张卡牌只能用一次。
【输入】
数据第一行两个数n1,n2,空格分割。
接下来n1行,每行3个数,依次表示每张X类卡牌的3项属性值。
接下来n2行,每行3个数,依次表示每张Y类卡牌的3项属性值。
【输出】
输出一个整数:最多能够匹配的数目。
【样例输入】
2 2
2 2 2
2 5 5
2 2 5
5 5 5
【样例输出】
2
【提示】
样例中第一张X类卡牌和第一张Y类卡牌能配对,第二张X类卡牌和两张Y类卡牌都能配对。所以最佳方案是第一张X和第一张Y配对,第二张X和第二张Y配对。
另外,请大胆使用渐进复杂度较高的算法!
【数据规模与约定】
对于10%的数据,n1,n2≤ 10;
对于50%的数据,n1,n2≤ 3000。
对于100%的数据,n1,n2≤ 30000,属性值为不超过200的正整数
首先,可以发现这是一个二分图匹配,只不过点有点多(多到你的匈牙利就算能跑(n^2)也会T)
最近刷了一堆题,题解都写的是:数据太水,xxx即可跑过 或是 显然,不会有这么极端的数据
所以我打了一个用vector存图的匈牙利算法就走了。
然后。。。。。。
我们要找方法优化,注意到连边时有一些性质,只要有两个位置上的数他们一一不互质即有边,
那么
我们采用将边分类的方法优化
将质因数有x的分一类
那么对于一些数,他们都满足:第一个数的质因数有x,第二个数的质因数有y
他们之间就要两两连边,十分浪费,建立一个中枢节点,每一条边就可以表示为
从一端点到中枢到另一端点两条边,就将(n^2)级别的边数转化为了O(n)
这种思想就类似于数据结构中的线段树,
新增一个节点来更好的管理下方数量庞大的节点,
将数据分类编织,
最终。。。。。。
被这个2000000条边的isap卡常数
我将isap与Dinic结合才通过了全部数据
AC code:
//华而不实的代码,T而不A的人生
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<cmath>
#include<ctime>
#define inf 0x3f3f3f3f
#define maxm 2000005
#define maxn 70005
using namespace std;
int n,m;
int d[3][50][50];//缺第i个位置,另外两个位置按顺序为j,k的点所联通
int Prev[maxm],to[maxm],info[maxn],cnt_e=1,cap[maxm];
int t1;
inline void get(int &res){
char ch;
while(!isdigit(ch=getchar()));
for(res=ch-'0';isdigit(ch=getchar());res=res*10+ch-'0');
}
inline void Node(int u,int v,int c){
cnt_e++;
Prev[cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v,cap[cnt_e]=c;
}
inline void Line(int u,int v,int c){
Node(v,u,0);
Node(u,v,c);
}
int tot,s,t;
int New(){
return ++tot;
}
int pr[50],cnt;
void prime(int up){
bool vis[205]={};
for(int i=2;i<=up;i++){
if(!vis[i])
pr[cnt++]=i;
for(int j=0;j<cnt && pr[j]*i<=up;j++)
{
vis[pr[j]*i]=1;
if(i%pr[j]==0) break;
}
}
s=New(),t=New();
for(int i=0;i<cnt;i++)
for(int j=0;j<cnt;j++)
for(int k=0;k<3;k++)
d[k][i][j]=New();
}
int c[3][5],tag[3];
inline void Divide(int *r,int &cntnum,int x){//r存放质因子,分解x
cntnum=0;
for(int i=0;i<cnt && pr[i]<=x;i++)
if(x%pr[i]==0)
r[cntnum++]=i;
}
bool flag;
int stm,dis[maxn],vd[maxn];
int Q[maxn],L,R;
inline bool BFS(){
L=R=0;
Q[R++]=t;
memset(dis,-1,sizeof dis);
dis[t]=0;vd[0]++;
int now;
while(L<R){
now=Q[L++];
for(int i=info[now];i;i=Prev[i])
if(cap[i^1] && dis[to[i]]==-1)
{
dis[to[i]]=dis[now]+1;
vd[dis[to[i]]]++;
Q[R++]=to[i];
}
}
return dis[s]!=-1;
}
int aug(int now,int Max){
if(now==t)
return Max;
int st=Max,inc,Min=tot-1;
for(int i=info[now];i;i=Prev[i])
if(cap[i]){
if(dis[to[i]]+1==dis[now]){
inc=aug(to[i],min(cap[i],st));
cap[i]-=inc;
cap[i^1]+=inc;
st-=inc;
if(!st || flag) return Max-st;
}
Min=min(Min,dis[to[i]]);
}
if(Max==st){
!(--vd[dis[now]]) && (flag=1);
vd[dis[now]=Min+1]++;
}
return Max-st;
}
int sap(){
memset(dis,0,sizeof d);
memset(vd,0,sizeof vd);
vd[0]=tot;
stm=flag=0;
BFS();
int t2=0;
while(!flag){
stm+=aug(s,inf);
//printf("%d\n",clock()-t1);
t2++;
if(t2%6==0) if(!BFS()) break;
}
return stm;
}
int main(){
//freopen("1.in","r",stdin);
int x,y,z;
prime(200);
get(n),get(m);
for(int i=1;i<=n;i++){
int u=New();
Line(s,u,1);
get(x),get(y),get(z);
Divide(c[0],tag[0],x);
Divide(c[1],tag[1],y);
Divide(c[2],tag[2],z);
for(int i=0;i<tag[0];i++)
for(int j=0;j<tag[1];j++)
Line(u,d[2][c[0][i]][c[1][j]],inf);
for(int i=0;i<tag[1];i++)
for(int j=0;j<tag[2];j++)
Line(u,d[0][c[1][i]][c[2][j]],inf);
for(int i=0;i<tag[0];i++)
for(int j=0;j<tag[2];j++)
Line(u,d[1][c[0][i]][c[2][j]],inf);
}
for(int i=1;i<=m;i++){
int u=New();
Line(u,t,1);
get(x),get(y),get(z);
Divide(c[0],tag[0],x);
Divide(c[1],tag[1],y);
Divide(c[2],tag[2],z);
for(int i=0;i<tag[0];i++)
for(int j=0;j<tag[1];j++)
Line(d[2][c[0][i]][c[1][j]],u,inf);
for(int i=0;i<tag[1];i++)
for(int j=0;j<tag[2];j++)
Line(d[0][c[1][i]][c[2][j]],u,inf);
for(int i=0;i<tag[0];i++)
for(int j=0;j<tag[2];j++)
Line(d[1][c[0][i]][c[2][j]],u,inf);
}
//printf("%d\n",clock()-t1);
printf("%d\n",sap());
//printf("%d\n",clock()-t1);
}