zoukankan      html  css  js  c++  java
  • [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('
    ');}
    
    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;
    }
    
  • 相关阅读:
    (4.7)怎么捕获和记录SQL Server中发生的死锁?
    SQLSERVER排查CPU占用高的情况
    (4.6)sql server索引缺失提示
    (4.14)向上取整、向下取整、四舍五入取整的实例
    mysql大致学习路径
    (2)linux未使用eth0,未使用IPV4导致无法连接
    (4.13)sql server参数嗅探(parameter sniffing)
    完美女人
    关于box-sizing
    什么是担当
  • 原文地址:https://www.cnblogs.com/hbyer/p/10504166.html
Copyright © 2011-2022 走看看