zoukankan      html  css  js  c++  java
  • 【BZOJ4405】[WC2016] 挑战NPC(带花树)

    点此看题面

    大致题意:(n)个球和(m)个筐,每个球可以放入某些筐中,且每个筐最多放(3)个球。现让你把所有球都放入筐中,问最多有多少个筐有不超过一个球。

    前言

    刚学带花树就来做这道题,不得不说实在是太妙了,根本想不到。。。

    拆点

    考虑我们把一个筐拆成三个点,然后在这三个点中两两连边,分别讨论一个筐中放不同数量球的情况:

    • 不放球:此时对答案贡献为(1),且一个筐拆成的三个点可以形成一个匹配。
    • (1)个球:此时对答案贡献为(1),且三个点中剩余的两个还可以形成一个匹配,共计两个匹配。
    • (2)个球:此时对答案贡献为(0),形成了两个匹配。
    • (3)个球:此时对答案贡献为(0),形成了三个匹配。

    整理一下即可发现一个巧妙的规律:一个筐的贡献=形成匹配数-放的球数。

    由于所有球都要放入筐中,因此放的球数之和就是(n)

    那么我们只要最大化形成匹配数之和,也就是求一般图最大匹配,则上带花树即可。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100
    #define M 300
    #define P 1000
    #define E 30000
    #define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
    using namespace std;
    int n,m,et,ee,lnk[P+5];struct edge {int to,nxt;}e[2*(3*E+3*M)+5];
    class FlowerMatchTree//带花树模板
    {
    	private:
    		int s[P+5],l[P+5],c[P+5],vis[P+5];queue<int> q;
    		int f[P+5];I int fa(CI x) {return f[x]?f[x]=fa(f[x]):x;}
    		I int LCA(RI x,RI y)
    		{
    			static int ti=0;++ti,x=fa(x),y=fa(y);
    			W(vis[x]^ti) vis[x]=ti,x=fa(l[s[x]]),y&&(x^=y^=x^=y);return x;
    		}
    		I void Blossom(RI x,RI y,CI p)
    		{
    			W(fa(x)^p) l[x]=y,y=s[x],!f[x]&&(f[x]=p),
    				!f[y]&&(f[y]=p),c[y]==2&&(q.push(y),c[y]=1),x=l[y];
    		}
    		I bool Match(CI x)//BFS
    		{
    			RI i;for(i=1;i<=n+3*m;++i) l[i]=c[i]=f[i]=0;W(!q.empty()) q.pop();
    			RI k,p;q.push(x),c[x]=1;W(!q.empty()) for(i=lnk[k=q.front()],q.pop();i;i=e[i].nxt)
    			{
    				if(fa(k)==fa(e[i].to)||c[e[i].to]==2) continue;
    				if(!c[e[i].to])
    				{
    					if(c[e[i].to]=2,l[e[i].to]=k,!s[e[i].to])
    						{for(k=e[i].to;k;k=p) p=s[l[k]],s[k]=l[k],s[l[k]]=k;return 1;}
    					c[s[e[i].to]]=1,q.push(s[e[i].to]);continue;
    				}
    				p=LCA(k,e[i].to),Blossom(k,e[i].to,p),Blossom(e[i].to,k,p); 
    			}return 0;
    		}
    	public:
    		I int operator [] (CI x) {return s[x];}
    		I int GetAns()//求一般图最大匹配
    		{
    			RI i,t=0;for(i=1;i<=n+3*m;++i) s[i]=0;
    			for(i=1;i<=n+3*m;++i) !s[i]&&Match(i)&&++t;return t;
    		}
    }T;
    int main()
    {
    	RI Tt,i,x,y;scanf("%d",&Tt);W(Tt--)
    	{
    		#define P(i,j) (n+3*((i)-1)+(j))//拆点
    		for(scanf("%d%d%d",&n,&m,&et),ee=0,i=1;i<=n+3*m;++i) lnk[i]=0;//清空
    		for(i=1;i<=et;++i) scanf("%d%d",&x,&y),add(x,P(y,1)),//向筐拆成的三个点连边
    			add(P(y,1),x),add(x,P(y,2)),add(P(y,2),x),add(x,P(y,3)),add(P(y,3),x);
    		for(i=1;i<=m;++i) add(P(i,1),P(i,2)),add(P(i,2),P(i,1)),//一个筐拆成的点间两两连边
    			add(P(i,1),P(i,3)),add(P(i,3),P(i,1)),add(P(i,2),P(i,3)),add(P(i,3),P(i,2));
    		for(printf("%d
    ",T.GetAns()-n),i=1;i<=n;++i) printf("%d%c",(T[i]-n+2)/3," 
    "[i==n]);//输出答案
    	}return 0;
    }
    
  • 相关阅读:
    生活
    通俗易懂----尾递归
    。。。
    调用startActivityForResult,onActivityResult无响应的解决办法
    安卓官方ViewPager与android.support.design.widget.TabLayout双向交互联动切换 。
    1、面向对象以及winform的简单运用(开篇)
    15、C#基础整理(递归)
    14、C#基础整理(函数)
    13、C#基础整理(枚举)
    12、C#基础整理(结构体)
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ4405.html
Copyright © 2011-2022 走看看