Codeforces 1220 D. Alex and Julian

在比赛结束后一分钟过题,前边的题目浪费了太长的时间

题意

给定一个集合B,让你从集合中去除最少的元素构造一个二分图,这个二分图的点和边都是无限的,点的编号为i与编号为j的节点连接当且仅当ijB|i - j|\in B.

分析与解答

我们考虑通过奇偶性来构造这个二分图,
如果集合B中全部都为奇数,我们知道奇数加上一个奇数为一个偶数,也就是说每条边都可以沟通一个全部为奇数的部分和全部为偶数的部分.
同样的如果在集合B中既存在奇数又存在偶数,因为奇数加偶数还为奇数,偶数加偶数还为偶数,那么我们不难发现一定存在矛盾.
如果集合B中全为偶数(一开始全为偶数也可以,结果发现想错了),需要考虑如下情况:
假设集合B为{2,4}\{2,4\},我们设1在左图中,那么3在右图中,从而5在左图中,但是根据B中有4我们可以得到5应该在右图中,出现矛盾.
但是如果集合B为{2,6}\{2,6\}我们就可以构造成功
于是我们考虑不成立的原因,发现只要在偶数中存在i,ji,j,而且存在a,ba,b奇偶性不同,同时满足ia=jbi * a = j * b,那么一定不行.而对于任何一个偶数来说一定是一个奇数与2的多少次方的乘积,如果a,ba,b奇偶性不同,那么能整除iijj最大二次幂一定不同.
因此对于偶数,我们去枚举二次幂,找到当前二次幂能表示的所有偶数,即为当前二次幂下能够组成二分图的最大的B的子集(也就是删除元素最少),找到所有二次幂下最大的那个子集,与奇数集相比较,取较大的那个为最后剩下的集合,其他为删除的元素.

代码

/*************************************************************************
	> File Name: 2019_10_6_4.cpp
	> Author: z472421519
	> Mail: 
	> Created Time: 2019年10月06日 星期日 16时51分29秒
 ************************************************************************/
 
#include <bits/stdc++.h>
#include <cstdio>
#include <cstring>
#define MAXN 200003
using namespace std;
 
long long a1[MAXN],a2[MAXN],x;
long long cal(int n)
{
    int sum = 0;
    long long sum_id = 2;
    long long now = 2LL;
    //memset(vis,false,sizeof(vis));
    #if 0
    for(int i = 1;i <= n;i++)
    {
 
    }
    #endif
    long long m = 0;
    for(int i = 1;i <= n;i++)
        m = max(a2[i],m);
    while(now <= m)
    {
        int res = 0;
        for(int i = 1;i <= n;i++)
        {
            if(a2[i] % now == 0)
            {
                a2[i] /= now;
                if(a2[i] % 2LL)
                {
                    res++;
                }
                a2[i] *= now;
            }
        }
        //now *= 2LL;
        if(res > sum)
        {
            sum = res;
            sum_id = now;
        }
        now *= 2LL;
    }
    return sum_id;
 
}
int main()
{
    int n;
    scanf("%d",&n);
    int sum1 = 0,sum2 = 0;
    for(int i = 1;i <= n;i++)
    {
        scanf("%I64d",&x);
        if(x % 2LL == 0)
        {
            a2[++sum2] = x;
        }
        else
        {
            a1[++sum1] = x;
        }
    }
    long long now = cal(sum2);
    //printf("%lld\n",now);
    //for(int i = 1;i <= )
    int res = 0;
    for(int i = 1;i <= sum2;i++)
        {
            if(a2[i] % now == 0)
            {
                a2[i] /= now;
                if(a2[i] % 2LL)
                {
                    res++;
                }
                a2[i] *= now;
            }
        }
    if(res > sum1)
    {
        printf("%d\n",n - res);
        for(int i = 1;i <= sum1;i++)
            if(a1[i] % now)
                printf("%I64d ",a1[i]);
        for(int i = 1;i <= sum2;i++)
            if(a2[i] % now || (a2[i] % now == 0 && (a2[i] / now) % 2LL == 0))
                printf("%I64d ",a2[i]);
    }
    else
    {
        printf("%d\n",sum2);
        for(int i = 1;i <= sum2;i++)
                printf("%I64d ",a2[i]);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章