Codeforces 1220D

1. 正确理解题意:

  • 简述该题:

给定两个集合,\(B=\{x,y,\dots\}\),\ \ \ \ \(Z={整数集}\)

如果集合 \(Z\) 中的两个元素 \(i, j\) 之差在集合 \(B\) 中,则有一条连接 \(i, j\) 的边。

现在要从集合 \(B\) 中删去一些元素,使得边所连成的图是一个二分图。

使得删去的元素尽可能少。

2. 思路及简单证明:

首先判断是不是二分图可以看图中是否有奇环

考虑一个特殊的整数 \(0\)

如果集合 \(B\) 中一个数 \(a\) 减去 \(0\) 所得还是 \(a\)。所以 \(a\)\(0\) 之间有一条边。

\(a\) 的因数 \(\frac{a}{2}\) 也在集合 %B% 中,那么 \(0\)\(a\)\(\frac{a}{2}\) 之间形成一个奇环。

因此我们在选中 \(a\) 后要将其倍数和因数全部删除。

也就是说在 \(0\)\(a\) 之间存在一个步数,步长是一个素数。我们走了 \(x\) 步从 \(0\)\(a\)

同理,我们也可以通过不同的步长和步数走到一个位置。现在假设两次都走到了 \(lcm\)

只有我们两边步数相同时,链接起来才不是奇环。(链接的地方在 \(lcm\)

3. 实现:

\(\mathcal{AC}\) 代码:

#include <iostream>
#include <cstdio>
#include <map>
#include <algorithm>

#define int long long
#define ri register int
using namespace std;
int n, a[200010], cnt[200010];
map<int,int> mp;
map<int,int>::iterator ans;

signed main()
{
  scanf("%lld", &n);
  for (ri i=1; i<=n; i++) {
    scanf("%lld", &a[i]);
    int x=a[i];
    while (x && x%2==0) {
      cnt[i]++;
      x/=2;
    }
    mp[cnt[i]]++;
  }
  int ansval=0;
  for (map<int,int>::iterator it=mp.begin(); it!=mp.end(); ++it) {
    if ((*it).second>ansval) {
      ans=it;
      ansval=(*it).second;
    }
  }
  printf("%lld\n", n-(*ans).second);
  for (ri i=1; i<=n; i++) {
    if (cnt[i]!=(*ans).first) {
      printf("%lld\n", a[i]);
    }
  }
  return 0;
}

参考博客:

https://www.cnblogs.com/luowentao/p/11550190.html

https://www.cnblogs.com/BakaCirno/p/11550454.html

posted @ 2020-02-19 23:12  敲可耐的螺旋藻  阅读(79)  评论(0编辑  收藏  举报