题目描述
桌子上现在有m张蓝色卡片和n张红色卡片,每张卡片上有一个大于1的整数。现在你要从桌子上拿走一些卡片,分若干次拿。每次只能拿走一组卡片:这组卡片颜色不同,并且两张卡片上面的数字的最大公约数大于1。问:最多可以从桌上拿走多少张卡片。
输入输出格式
输入格式:每个输入文件中包含多组测试数据,每个文件中测试数据的数目不超过100。
文件的第一行读入一个整数T,为数据组数。
每组数据的格式如下:
m n
b1 b2 … bm
r1 r2 … rn
第二行给出每张蓝色卡片上面的数字,第三行给出每张红色卡片上的数字。
对每组测试数据,输出最多可以拿走多少张卡片。
输入输出样例
输入样例#1:
7 4 3 2 6 6 15 2 3 5 2 3 4 9 8 16 32 4 2 4 9 11 13 5 7 5 5 2 3 5 1001 1001 7 11 13 30 30 10 10 2 3 5 7 9 11 13 15 17 29 4 6 10 14 18 22 26 30 34 38 20 20 195 144 903 63 137 513 44 626 75 473 876 421 568 519 755 840 374 368 570 872 363 650 155 265 64 26 426 391 15 421 373 984 564 54 823 477 565 866 879 638 100 100 195 144 903 63 137 513 44 626 75 473 876 421 568 519 755 840 374 368 570 872 363 650 155 265 64 26 426 391 15 421 373 984 564 54 823 477 565 866 879 638 117 755 835 683 52 369 302 424 513 870 75 874 299 228 140 361 30 342 750 819 761 123 804 325 952 405 578 517 49 457 932 941 988 767 624 41 912 702 241 426 351 92 300 648 318 216 785 347 556 535 166 318 434 746 419 386 928 996 680 975 231 390 916 220 933 319 37 846 797 54 272 924 145 348 350 239 563 135 362 119 446 305 213 879 51 631 43 755 405 499 509 412 887 203 408 821 298 443 445 96 274 715 796 417 839 147 654 402 280 17 298 725 98 287 382 923 694 201 679 99 699 188 288 364 389 694 185 464 138 406 558 188 897 354 603 737 277 35 139 556 826 213 59 922 499 217 846 193 416 525 69 115 489 355 256 654 49 439 118 961
输出样例#1:
3 1 0 4 9 18 85
说明
对100%的数据:1<=t<=100;
1<=m,n<=500;
卡片上的数字大于1,小于10 000 000。
裸的网络流。。。
判断两两数字的 gcd 是否大于1,连边。
然而会TLE。。。为什么?
n^2枚举,加上n^2*m网络流,当然T。。。
怎么办?
我们发现数值是小于10^7的,于是我们可以将每个数分解质因数,数字与其质因数连边,再跑网络流,即可AC。
附代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<queue>
#include<cstring>
#define MAXN 100010
#define MAXM 10000010
#define MAX 999999999
using namespace std;
int n,m,k=0,s,t,c;
int x[MAXN],y[MAXN],head[MAXN<<1],deep[MAXN<<1],prime[MAXM];
bool np[MAXM];
struct node{
int next,to,w;
}a[MAXM/10+9];
inline int read(){
int date=0,w=1;char c=0;
while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
return date*w;
}
inline void add(int u,int v,int w){
a[c].to=v;a[c].w=w;a[c].next=head[u];head[u]=c++;
a[c].to=u;a[c].w=0;a[c].next=head[v];head[v]=c++;
}
bool bfs(){
int u,v;
queue<int> q;
for(int i=s;i<=t;i++)deep[i]=0;
deep[s]=1;
q.push(s);
while(!q.empty()){
u=q.front();
q.pop();
for(int i=head[u];i;i=a[i].next){
v=a[i].to;
if(a[i].w&&!deep[v]){
deep[v]=deep[u]+1;
if(v==t)return true;
q.push(v);
}
}
}
return false;
}
int dfs(int x,int limit){
if(x==t)return limit;
int v,sum,cost=0;
for(int i=head[x];i;i=a[i].next){
v=a[i].to;
if(a[i].w&&deep[v]==deep[x]+1){
sum=dfs(v,min(a[i].w,limit-cost));
if(sum>0){
a[i].w-=sum;
a[i^1].w+=sum;
cost+=sum;
if(cost==limit)break;
}
else deep[v]=-1;
}
}
return cost;
}
int dinic(){
int ans=0;
while(bfs())ans+=dfs(s,MAX);
return ans;
}
inline void linkx(int x,int id){
for(int i=1;i<=k&&prime[i]<=x;i++){
if(x%prime[i]==0){
add(id,i+n+m,MAX);
t=max(t,i+n+m);
while(x%prime[i]==0)x/=prime[i];
}
}
}
inline void linky(int y,int id){
for(int i=1;i<=k&&prime[i]<=y;i++){
if(y%prime[i]==0){
add(i+n+m,id,MAX);
t=max(t,i+n+m);
while(y%prime[i]==0)y/=prime[i];
}
}
}
void work(){
int u,v,w;
n=read();m=read();
for(int i=1;i<=n;i++){
x[i]=read();
linkx(x[i],i);
}
for(int i=1;i<=m;i++){
y[i]=read();
linky(y[i],i+n);
}
s=0;t++;
for(int i=1;i<=n;i++)add(s,i,1);
for(int i=1;i<=m;i++)add(i+n,t,1);
printf("%d\n",dinic());
}
inline void init(){
c=2;
memset(head,0,sizeof(head));
}
void make(){
int x=MAXM-10;
np[0]=np[1]=true;
for(int i=2;i<=x;i++){
if(!np[i])prime[++k]=i;
for(int j=1;j<=k&&prime[j]*i<=x;j++){
np[prime[j]*i]=true;
if(i%prime[j]==0)break;
}
}
}
int main(){
int cases=read();
make();
while(cases--){
init();
work();
}
return 0;
}