P4258 [WC2016]挑战NPC(一般图匹配)
Posted Harris-H
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P4258 [WC2016]挑战NPC(一般图匹配)相关的知识,希望对你有一定的参考价值。
P4258 [WC2016]挑战NPC(一般图匹配)
很妙,一个筐最多装3个球,显然是拆点,但是贡献只有半筐(球数小于2)。
考虑建图的时候,把三个筐点相互连边。
这样筐0个球,内部产生一个匹配,贡献为1。实际贡献为1
1个球,内部产生一个匹配,贡献为2。实际贡献为1
2个球,内部产生0个匹配,贡献为2。实际贡献为0
3个球,内部产生0个匹配,贡献为3.实际贡献为0.
观察发现,贡献 - 匹配的球数 = 实际贡献。
这样跑一遍一般图匹配,然后减去总球数 n n n即可。
输出注意还原点的编号。
// Problem: P4258 [WC2016]挑战NPC
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P4258
// Memory Limit: 250 MB
// Time Limit: 1000 ms
// Date: 2021-11-23 14:38:06
// --------by Herio--------
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=2e4+5,M=505,inf=0x3f3f3f3f,mod=1e9+7;
const int hashmod[4] = 402653189,805306457,1610612741,998244353;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define PLL pair<ll,ll>
#define x first
#define y second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define per(i,a,b) for(int i=a;i>=b;--i)
#define ios ios::sync_with_stdio(false),cin.tie(nullptr)
void Print(int *a,int n)
for(int i=1;i<n;i++)
printf("%d ",a[i]);
printf("%d\\n",a[n]);
template <typename T> //x=max(x,y) x=min(x,y)
void cmx(T &x,T y)
if(x<y) x=y;
template <typename T>
void cmn(T &x,T y)
if(x>y) x=y;
struct Blossom_Tree
vector<int>e[N];
queue<int>q;
int mh[N],pre[N],s[N],id[N],vis[N],n,num;
void init(int nn) //初始化
n=nn,num=0;
for(int i=1;i<=n;i++) e[i].clear(),mh[i]=pre[i]=vis[i]=s[i]=id[i]=0;
void add(int u,int v)
e[u].push_back(v),e[v].push_back(u);
int find(int x) return s[x]==x?x:s[x]=find(s[x]);
int LCA(int x,int y) //求LCA.
for(++num;;swap(x,y))
if(x)
if(id[x=find(x)]==num) return x;//在同一个奇环肯定能找到被编号的结点.
id[x]=num,x=pre[mh[x]]; //沿增广路径的非匹配边向上走直到找到祖先.
void blossom(int x,int y,int rt) //缩点(开花) O(n)
while(find(x)!=rt)
pre[x]=y,y=mh[x];
if(vis[y]==2) vis[y]=1,q.push(y);//如果奇环上有2型结点 就变为1型结点加入队列.
if(find(x)==x) s[x]=rt;//只对根处理.
if(find(y)==y) s[y]=rt;
x=pre[y];
bool aug(int st) //求增广路径.O(n)
for(int i=1;i<=n;i++) vis[i]=pre[i]=0,s[i]=i;
while(!q.empty()) q.pop();
q.push(st),vis[st]=1;
while(!q.empty())
int u=q.front();q.pop();
for(auto v:e[u])
if(find(u)==find(v)||vis[v]==2) continue;//如果是已经缩点了或者是偶环直接继续,没有影响.
if(!vis[v]) //如果没有被染色
vis[v]=2,pre[v]=u;
if(!mh[v]) //如果未被匹配,就进行匹配
for(int x=v,last;x;x=last) //增广路取反
last=mh[pre[x]],mh[x]=pre[x],mh[pre[x]]=x;
return 1;
vis[mh[v]]=1,q.push(mh[v]); //否则将v的匹配结点染色,加入队列.
else
int rt=LCA(u,v); //找到LCA ,然后进行缩点.
blossom(u,v,rt),blossom(v,u,rt);
return 0;
int cal()
int ans=0;
for(int i=1;i<=n;i++) if(!mh[i]&&aug(i)) ans++; //最大匹配.
return ans;
T;
int main()
int TT;scanf("%d",&TT);while(TT--)
int n,m,e;scanf("%d%d%d",&n,&m,&e);
T.init(3*m+n);
rep(i,1,e)
int u,v;scanf("%d%d",&u,&v);
int x=n+(v-1)*3;
T.add(u,x+1);
T.add(u,x+2);
T.add(u,x+3);
rep(i,1,m)
int x=n+(i-1)*3;
T.add(x+1,x+2),T.add(x+1,x+3),T.add(x+2,x+3);
printf("%d\\n",T.cal()-n);
rep(i,1,n)
printf("%d ",(T.mh[i]-n-1)/3+1);
puts("");
return 0;
以上是关于P4258 [WC2016]挑战NPC(一般图匹配)的主要内容,如果未能解决你的问题,请参考以下文章