Bzoj4205卡牌配对

原题网址:http://www.lydsy.com/JudgeOnline/problem.php?id=4205
大致模型是二分图最大匹配,但暴力建边会T,考虑建中间节点,至少要有两项属性值不互质,但这样中间点数还是爆炸和边数,然而看了题解才知道中间点只要质数即可。(一开始反向边流量又忘记设为0了。。。
%hzwer http://hzwer.com/7428.html

#include<bits/stdc++.h>
const int N = 7e4;
const int M = 2e6;
const int INF = 1e9;
template <typename T> void read(T &x){
    x = 0; T f = 1;
    char c = getchar();
    while (c < '0' || c > '9') {if (c == '-') f *= -1; c = getchar();}
    while (c >= '0' && c <= '9') {x = x * 10 + c - '0'; c = getchar();}
    x *= f;
}
struct edge{int y,v,next;} mp[M*2];
int n1,n2,S,T,first[N],cur[N],p[N],a[N],b[N],c[N],h[N],q[N],x[N],y[N],z[N],s,cnt,ans;
void ins(int x, int y, int v){
    mp[++s] = (edge) {y,v,first[x]}; first[x] = s;
    mp[++s] = (edge) {x,0,first[y]}; first[y] = s;
}
bool isprime(int n){
    if (n == 1) return 0;
    for (int i=2; i*i<=n; i++)
        if (n % i == 0)
            return 0;
    return 1;
}
void init(){
    for (int i=1; i<=200; i++)
        if (isprime(i)) p[cnt++] = i;
    read(n1);read(n2);
    for (int i=1; i<=n1+n2; i++){
        read(a[i]);read(b[i]);read(c[i]);
    }
    s = 1;
}
void build(){
    S = 0; T = n1 + n2 + 46 * 46 * 3 + 1;
    for (int i=1; i<=n1; i++) ins(S,i,1);
    for (int i=n1+1; i<=n1+n2; i++) ins(i,T,1);
    int t;
    for (int k=1; k<=n1+n2; k++){
        x[0] = y[0] = z[0] = 0;
        for (int i=0; i<46; i++){
            if (a[k] % p[i] == 0) x[++x[0]] = i;
            if (b[k] % p[i] == 0) y[++y[0]] = i;
            if (c[k] % p[i] == 0) z[++z[0]] = i;
        }
        t = n1 + n2 + 1;
        for (int i=1; i<=x[0]; i++)
            for (int j=1; j<=y[0]; j++)
                if (k<=n1) ins(k,t+x[i]*46+y[j],INF);
                else       ins(t+x[i]*46+y[j],k,INF);
        t += 46 * 46;
        for (int i=1; i<=x[0]; i++)
            for (int j=1; j<=z[0]; j++)
                if (k<=n1) ins(k,t+x[i]*46+z[j],INF);
                else       ins(t+x[i]*46+z[j],k,INF);
        t += 46 * 46;
        for (int i=1; i<=y[0]; i++)
            for (int j=1; j<=z[0]; j++)
                if (k<=n1) ins(k,t+y[i]*46+z[j],INF);
                else       ins(t+y[i]*46+z[j],k,INF);
    }
}
bool bfs(){
    for (int i=S; i<=T; i++) h[i] = -1, cur[i] = first[i];
    int head = 1, tail = 1;
    h[q[head] = S] = 1;
    for (; head<=tail; head++){
        int x = q[head];
        for (int t=first[x]; t>0; t=mp[t].next)
            if (mp[t].v && h[mp[t].y] == -1)
                h[q[++tail] = mp[t].y] = h[x] + 1;
    }
    return h[T] != -1;
}
int dfs(int x, int fl){
    if (x == T) return fl;
    int used = 0, b;
    for (int t=cur[x]; t>0; t=cur[x]=mp[t].next){
        if (h[mp[t].y] != h[x] + 1) continue;
        used += b = dfs(mp[t].y,std::min(fl-used,mp[t].v));
        mp[t].v -= b;
        mp[t^1].v += b;
        if (used == fl) return used;
    }
    if (!used) h[x] = -1;
    return used;
}
int main(){
    init();
    build();
    while (bfs()) ans += dfs(S,INF);
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值