• 模拟赛AT2046 Namori 二分图


    题面

    Description
    ​ 现在给你一张NN个点MM条边的连通图,我们保证N−1≤M≤NN−1≤M≤N,且无重边和自环。
    ​ 每一个点都有一种颜色,非黑即白。初始时,所有点都是白色的。
    ​ 想通过执行若干次某种操作的方式,来将所有的点变成黑色。操作方式如下:
    ​ 选择一对颜色相同的相邻的节点(存在边直接连接彼此),将它们的颜色反转。即若原来都是白色,则都变成黑色,反之亦然。
    ​ 现在想知道,他能否通过执行这种操作以达到目的。如果可以,他还希望步数尽可能的少。

    Input
    ​ 第一行有两个正整数NN和MM(2≤N≤1052≤N≤105,N−1≤M≤NN−1≤M≤N)
    接下来MM行,每行2个正整数aa和bb(1≤a,b≤N1≤a,b≤N),表示每条边连接的两个点。

    Output
    ​ 如果存在操作方案能达到目的,请输出最少操作次数。
    否则,请输出−1

    解法

    先考虑树

    大佬们说这是一个套路:看到对相同颜色的点进行操作,就把他们建成不同颜色。
    于是我们就先套路,对树进行分层黑白染色,于是原问题转化为进行多少次操作,可以让整棵树上颜色对调。
    如果黑白颜色个数不同显然不行。
    考虑一个子树的根,他和他父亲最少要操作子树内黑白颜色的差次,对于每一个节点维护这样一个S_i,
    答案的下界就是S_i的绝对值的和,显然这个下界是可以取到的。

    奇环

    断开一条换上的边,两点是同色的,也就是说可以对这条边进行操作凭空让黑点或白点个数增加(将原来的白点看成白点,黑点看成白点理解即可)
    如果此时黑白点个数不平衡,且差为偶数,就可以同过这条边平衡。
    先断开这条边,计入答案和S_i,再维护S_i即可。

    偶环

    假设这条边转了x次,答案为$sum$ abs(Si-k*x),k只为1、-1或0,用中位数定理可求得最优的x

    代码

    #include<bits/stdc++.h>
    
    using namespace std;
    typedef long long LL;
    const int N=1e5+5;
    
    int head[N],nxt[N<<1],to[N<<1],siz[N],z[N],k[N];
    int n,m,num,X,Y,odd,sum,tot,mid;
    LL ans;
    
    int read()
    {
        int x=0,p=1; char ch=getchar();
        while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
        if(ch=='-') p=-1,ch=getchar();
        while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
        return x*p;
    }
    
    void add(int x,int y)
    {
    	nxt[++num]=head[x],to[num]=y,head[x]=num;
    }
    
    void dfs1(int x,int fa)
    {
    	for(int i=head[x]; i ;i=nxt[i])
    	{
    		int y=to[i];
    		if(y==fa) continue ;	
    		if(siz[y])
    		{
    			if(siz[y]==siz[x]) odd=1;
    			X=x,Y=y;
    		}
    		else
    		{
    			siz[y]=-siz[x];
    			dfs1(y,x);
    		}
    	}
    }
    
    void dfs2(int x,int fa)
    {
    	for(int i=head[x]; i ;i=nxt[i])
    	{
    		int y=to[i];
    		if(y==fa || (x==X&&y==Y) || (x==Y&&y==X))
    			continue ;
    		dfs2(y,x);
    		siz[x]+=siz[y];
    		k[x]+=k[y];
    	}
    }
    
    int main()
    {
        //freopen(".in","r",stdin);
        //freopen(".out","w",stdout);
    
    	n=read(),m=read();
    	for(int i=1;i<=m;i++)
    	{
    		int x=read(),y=read();
    		add(x,y),add(y,x);
    	}
    
    	siz[1]=1;
    	dfs1(1,0);
    	for(int i=1;i<=n;i++) sum+=siz[i];
    
    	if(n-1==m)
    	{ if(sum) return printf("-1
    "),0; }
    	else
    		if(odd)
    		{
    			if(sum&1) return printf("-1
    "),0;
    			ans+=abs(sum>>1); 
    			siz[X]-=sum>>1,siz[Y]-=sum>>1;
    		}
    		else
    		{
    			if(sum) return printf("-1
    "),0;
    			k[X]=1,k[Y]=-1;
    		}
    
    	dfs2(1,0);
    	for(int i=1;i<=n;i++)
    		if(k[i]) z[++tot]=k[i]*siz[i];
    		else ans+=abs(siz[i]);
    	z[++tot]=0;
    	sort(z+1,z+tot+1);
    	mid=z[tot+1>>1];
    	for(int i=1;i<=tot;i++) ans+=abs(z[i]-mid);
    
    	printf("%lld
    ",ans);
        return 0;
    }
    

    被if、else的嵌套卡了好久。。。

  • 相关阅读:
    B-Tree(B树)原理及C++代码实现
    Select(快速选择顺序统计量)原理及C++代码实现
    BucketSort(桶排序)原理及C++代码实现
    RadixSort(基数排序)原理及C++代码实现
    CountingSort(计数排序)原理及C++代码实现
    面向对象之封装
    今日算法题
    面向对象之抽象类和接口
    面向对象之多态
    今日算法题
  • 原文地址:https://www.cnblogs.com/lzqlalala/p/10479208.html
Copyright © 2020-2023  润新知