您的位置:首页 > 其它

【GDOI模拟】染色配对

2016-04-10 00:51 405 查看

Description



Solution

比赛的时候没有时间打这一题了。

回想一下才发现,是我昨天才做过的原题的类型题。

小N研究的NP完全问题jzoj上面叫A……

其实题意是在一个图中出最多的边,并且他们没有交集。

因为每一个点只会在匹配中最多出现一次,那么我们就要选择这些点都要选哪一些极大团。

我们一开始先让这些点随便选极大团,然后要他们可以选的两个极大团互相两边,表示这个点可以从这个极大团转移到另一个去,一条边也可以表示成一个点。

给极大团连完边之后,就会发现联通块的最大匹配的答案为边数除以2,因为两两的点要连边。

然后第一个答案就很好解了。

对于第二些答案,与小N研究的NP完全问题的做法极像。

因为有一些极大团中会有奇数个点,这样就不能匹配完,那么我们需要把这些点给转移走,那么我们就要考虑,每个点是要在那个极大团中进行匹配。

搜出一棵dfs树之后,如果某个节点i的子树的奇数节点的和是奇数,那么节点i就需要向上转移走一个点了,那么就是他与上面连接的这条边表示的节点选择上面的极大团,否则选择方向不变。

当极大团中的点确定之后,随意两两匹配就可以了。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
const int maxn=200007;
using namespace std;
int i,j,k,l,t,n,m,ans;
int first[maxn*2],next[maxn*2],last[maxn*2],num,shu[maxn*2];
int a[maxn*2],b[maxn*2],c[maxn*2],d[maxn*2],tot,ans1[maxn][2];
bool bz[maxn];
void add(int x,int y,int z){
last[++num]=y;
next[num]=first[x];
first[x]=num;
shu[num]=z;
}
void add1(int x,int y){
last[++num]=y;
next[num]=first[x];
first[x]=num;
}
int dfs(int x){
int i,j=c[x]%2,k;
bz[x]=1;
for(i=first[x];i;i=next[i]){
if(!bz[last[i]]){
k=dfs(last[i]);
d[shu[i]]=k%2;
j+=k;
}
}
return j;
}
int dfs1(int x){
int i,j=0;
bz[x]=1;
for(i=first[x];i;i=next[i]){
if(!bz[last[i]]){
j+=dfs1(last[i])+1;
}
else j++;
}
return j;
}
int main(){
scanf("%d%d",&n,&m);
fo(i,1,m){
scanf("%d%d",&a[i],&b[i]);
c[a[i]]++;
}
fo(i,1,m){
add(a[i],b[i],i);
add(b[i],a[i],i);
}
fo(i,1,n){
if(!bz[i])ans+=dfs1(i)/4;//这里是处理了双向边
}
printf("%d\n",ans);
memset(bz,0,sizeof(bz));
fo(i,1,n){
if(!bz[i])t=dfs(i);
}
t=0;
memset(first,0,sizeof(first));num=0;
fo(i,1,m){
if(d[i]%2==0){
add1(a[i],i);
}
else{
add1(b[i],i);
}
}
fo(i,1,n){
int u=0;
for(j=first[i];j;j=next[j]){
u=last[j];
j=next[j];
if(!j)break;
printf("%d %d\n",u,last[j]);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: