洛谷P2065 [TJOI2011]卡片

题目描述

桌子上现在有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
#include
#include
#include
#include
#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 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;
}

你可能感兴趣的:(网络流,数论)