【GDOI模拟】染色配对

Description

【GDOI模拟】染色配对_第1张图片

Solution

比赛的时候没有时间打这一题了。
回想一下才发现,是我昨天才做过的原题的类型题。
小N研究的NP完全问题jzoj上面叫A……
其实题意是在一个图中出最多的边,并且他们没有交集。
因为每一个点只会在匹配中最多出现一次,那么我们就要选择这些点都要选哪一些极大团。
我们一开始先让这些点随便选极大团,然后要他们可以选的两个极大团互相两边,表示这个点可以从这个极大团转移到另一个去,一条边也可以表示成一个点。
给极大团连完边之后,就会发现联通块的最大匹配的答案为边数除以2,因为两两的点要连边。
然后第一个答案就很好解了。
对于第二些答案,与小N研究的NP完全问题的做法极像。
因为有一些极大团中会有奇数个点,这样就不能匹配完,那么我们需要把这些点给转移走,那么我们就要考虑,每个点是要在那个极大团中进行匹配。
搜出一棵dfs树之后,如果某个节点i的子树的奇数节点的和是奇数,那么节点i就需要向上转移走一个点了,那么就是他与上面连接的这条边表示的节点选择上面的极大团,否则选择方向不变。
当极大团中的点确定之后,随意两两匹配就可以了。

Code

#include
#include
#include
#include
#include
#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]);    
        }
    }
}

你可能感兴趣的:(GDOI,贪心,网络流)