CF1198F GCD Groups Solution

给个随机化程度非常高的题解 ...
不用 random_shuffle,代价就是慢得离谱。
原因大概是用了巨大多暴力的状压。

说实在的我这篇题解在给大家踩坑 ...
码量高达 5K 而且非常难调试,例如 dpd 弄混。


很显然地,随便 rand 两个数,这两个数要么是同一个集合,要么不是一个集合。
那么现在方便考虑,我们默认它们不是同一个集合的。
因为如果是同一个集合我们完全可以再 rand 一定次数得到。

然后最重要的部分就是怎么分集合了。

首先把两个数质因数分解没问题吧。。
然后我们得出了这两个数的所有质因子。

接着我们想一个集合存在两个数互质,是这个集合内 (gcd=1) 的充分条件。
所以现在就找这样的两个数。

经过验算发现一个数的质因子个数最多不超过 (9) 个的样子,然后我们就可以大胆开数组记录这个序列中,除了 rand 出来的两个坐标 (x)(y) 之外,其它数和这两个数的公因数情况。

其实总的下来这个跟离散化因子差不多意思。。。

处理完之后我们就可以开始暴力状压了~

保存了共有因子情况之后我们可以设 dp[maski][maskj] 表示对于两个集合的 (gcd) 情况来说,到达这个状态的方式是更改第一个集合还是第二个集合。
这个时候就要记录路径了,而且还比较麻烦,但是转移方程很简单 ...

那么这样为什么是对的呢?首先很显然地,包含 (a_x) 的集合的 (gcd) 只可能是 (a_x) 的因数,对吧?所以我们可以只记录其因子的取法而没有必要看其它根本不可能作为答案的因子。另外,这里的 mask 转移方法就是对于 maskimaskj 分别取 and 转移,当然,无法到达的状态赋值为 -1,这里判断一下无解就可以了。

然后就没了。。。

里面加了一些优化,有判断 rand 重复的 stk,以及 size 的限制和时间限制的利用,如果 TLE 了可以尝试用一下?那大概就是这样了,另外我怕数据小的时候正确性无法保证,我直接状压。。

#include <bits/stdc++.h>
using namespace std;
#define int long long

namespace Gen {
  unsigned z1, z2, z3, z4, bas;
  inline unsigned random(int md) {
    bas = ((z1 << 6u) ^ z1) >> 13u;
    z1 = ((z1 & 4294967294u) << 18) ^ bas;
    bas = ((z2 << 2u) ^ z2) >> 27u;
    z2 = ((z2 & 4294967288u) << 2u) ^ bas;
    bas = ((z3 << 13u) ^ z3) >> 21u;
    z3 = ((z3 & 4294967280u) << 7u) ^ bas;
    bas = ((z4 << 3u) ^ z4) >> 12u;
    z4 = ((z4 & 4294967168u) << 13u) ^ bas;
    unsigned cur = z1 ^ z2 ^ z3 ^ z4;
    return cur % (unsigned)md;
  }

  inline void Srand(unsigned x) {
    z1 = x; z2 = (~x) ^ 0x233333333u;
    z3 = x ^ 0x1234598766u; z4 = (~x) + 51;
    return ;
  }
}

const int N = 1e5 + 10;
const int M = 1 << 10;
typedef pair <int, int> pii;
#define mp make_pair
#define ins insert
#define LL long long
#define pb push_back

set <pii> stk;
int n, a[N], dp[M][M];
int prime[N][2], d[M][M];
int p[M][M], part[N], cnt[11][2];
vector <int> pr[2];

inline void read(int &ret) {
  ret = 0; char ch = getchar();
  while (!isdigit(ch)) ch = getchar();
  while (isdigit(ch)) {
    ret = (ret << 1) + (ret << 3) + (ch ^ 48);
    ch = getchar();
  } return ;
}

clock_t Start = clock();
inline bool TLE() { return clock() - Start > 0.485 * CLOCKS_PER_SEC; }
inline int gcd(int x, int y) { return !y? x : gcd(y, x%y); }
mt19937 rng(1999999973);

signed main() {
  read(n); using namespace Gen;
  Srand(19260817u);
  for (int i = 0; i < n; ++i)
    read(a[i]), stk.ins(mp(i, i));
  if (n <= 20) {
    for (int mask = 1; mask < (1 << n) - 1; ++mask) {
      int msk1 = 0, msk2 = 0;
      for (int i = 0; i < n; ++i)
        if ((mask >> i) & 1) msk1 = gcd(msk1, a[i]);
        else msk2 = gcd(msk2, a[i]);
      if (msk1 == 1 && msk2 == 1) {
        puts("YES");
        for (int i = 0; i < n; ++i)
          putchar((mask >> i) & 1 ? '1' : '2'), putchar(' ');
        return 0;
      }
    }
    puts("NO");
    return 0;
  }
  do {
    int posx = random(n), posy = random(n);
    while (stk.find(mp(posx, posy)) != stk.end())
      posx = random(n), posy = random(n);
    stk.ins(mp(posx, posy)); stk.ins(mp(posy, posx));

    pr[0].clear(), pr[1].clear(); int cur = a[posx];
    for (int i = 2; i * i <= a[posx]; ++i) {
      if (!(cur % i)) pr[0].pb(i);
      while (!(cur % i)) cur /= i;
    }
    if (cur != 1) pr[0].pb(cur);

    cur = a[posy];
    for (int i = 2; i * i <= a[posy]; ++i) {
      if (!(cur % i)) pr[1].pb(i);
      while (!(cur % i)) cur /= i;
    }
    if (cur != 1) pr[1].pb(cur);

    int Lenpr = pr[0].size(), Len_pr = pr[1].size();
    for (int i = 0; i < Lenpr; ++i) cnt[i][0] = 0;
    for (int i = 0; i < Len_pr; ++i) cnt[i][1] = 0;

    vector <int> curans;
    for (int i = 0; i < n; ++i) {
      bool ok = false;
      if (i == posx || i == posy) continue;
      for (int j = 0; j < Lenpr; ++j)
        if (a[i] % pr[0][j] && cnt[j][0] - 5 < Len_pr)
          ok = true;
      for (int j = 0; j < Len_pr; ++j)
        if (a[i] % pr[1][j] && cnt[j][1] - 5 < Lenpr)
          ok = true;
      if (!ok) continue; curans.pb(i);
      for (int k = 0; k < 2; ++k) {
        for (int j = 0; j < pr[k].size(); ++j) {
          cnt[j][k] += (a[i] % pr[k][j] != 0);
          prime[i][k] |= ((a[i] % pr[k][j] == 0) << j);
        }
      }
    }

    for (int i = 0; i < (1LL << Lenpr); ++i)
      for (int j = 0; j < (1LL << Len_pr); ++j)
        dp[i][j] = d[i][j] = p[i][j] = -1;
    dp[(1LL << Lenpr) - 1][(1LL << Len_pr) - 1] = 0;

    for (int i = 0; i < curans.size() && dp[0][0] == -1; ++i) {
      int msk1 = prime[curans[i]][0];
      int msk2 = prime[curans[i]][1];
      for (int mask1 = 0; mask1 < (1LL << Lenpr); ++mask1) {
        for (int mask2 = 0; mask2 < (1LL << Len_pr); ++mask2) {
          if (dp[mask1][mask2] == -1 || d[mask1][mask2] == curans[i]) continue;
          if ((msk1 & mask1) != mask1 && dp[msk1 & mask1][mask2] == -1) {
            dp[msk1 & mask1][mask2] = 0;
            d[msk1 & mask1][mask2] = curans[i];
            p[msk1 & mask1][mask2] = mask1;
          }
          if ((msk2 & mask2) != mask2 && dp[mask1][msk2 & mask2] == -1) {
            dp[mask1][msk2 & mask2] = 1;
            d[mask1][msk2 & mask2] = curans[i];
            p[mask1][msk2 & mask2] = mask2;
          }
        }
      }
    }

    if (dp[0][0] == -1) continue;
    part[posx] = 0, part[posy] = 1; int msk1 = 0, msk2 = 0;
    while (mp(msk1, msk2) != mp((1LL << Lenpr) - 1, (1LL << Len_pr) - 1)) {
      part[d[msk1][msk2]] = dp[msk1][msk2];
      if (!dp[msk1][msk2]) msk1 = p[msk1][msk2];
      else msk2 = p[msk1][msk2];
    }

    puts("YES");
    for (int i = 0; i < n; ++i) printf("%lld ", part[i] + 1);
    return 0;
  } while (!TLE() && (LL)stk.size() < (LL)n * n);
  puts("NO"); return 0;
}
原文地址:https://www.cnblogs.com/lbn233/p/CF1198F-Sol.html