P17142 [NOI 2026] 布丁

对第一步爬山,不用加任何剪枝,估价为划分出等价类大小最大值,每次加一个数删一个数。在 \(len=10\) 时容易搞出 \(\max=84\) 的解,当然多跑一会可以更小。q2s94

初始取 \(len=9\),可以先爬山出来 \(\max=80\) 的解。然后对每个等价类分别退火,钦定选 \(6\) 个数,等几分钟就有 \(q=3,s\le30\) 的解,直接打表就好。q3s29

稍微精细一点,把次数多的等价类拉出来单独一个个退火,每个多跑一下,跑不出来可以改一下集合大小。q3s26

upd:最后一次把所有数都问一遍,这里可以把最大值删掉,可以再少一次。q3s25

退火代码
#include <bits/stdc++.h>
using namespace std;

const int inf = 1e9;
template <typename T>
bool Chkmax(T &x, T y) { return (x < y) ? (x = y, 1) : 0; }
template <typename T>
bool Chkmin(T &x, T y) { return (x > y) ? (x = y, 1) : 0; }

mt19937_64 gen (random_device{} ());
int rnd(int l, int r) {
  return uniform_int_distribution<> (l, r) (gen);
}
double rd() {
  return uniform_real_distribution<> (0, 1) (gen);
}

const int kN = 9005;
int n, m, V, type, mn = inf, mx;
int g[kN][kN];
bitset<kN> can;
int num[kN], tot[kN];
bool in[kN];
vector<int> all;
vector<int> vec, ans;

bool IsPrime(int x) {
  if(x <= 1) return 0;
  for(int i = 2; i * i <= x; i++) {
    if(!(x % i)) return 0;
  }
  return 1;
}

int D(int x) {
  int d = 0;
  for(int i = 1; i * i <= x; i++) {
    if(!(x % i)) {
      d += 2 - (i * i == x);
    }
  }
  return d;
}

int New() { return rnd(1, V); }

void Upd(int x, int v) {
  x += V;
  tot[num[x]]--;
  tot[num[x] += v]++;
  if(tot[mx + 1]) mx++;
  else if(!tot[mx]) mx--;
}

void UpdSeg(int l, int r, int v) {
  for(int i = can._Find_next(l); i < r; i = can._Find_next(i)) {
    Upd(g[l][i] + g[r][i] - g[l][r], v);
  }
}

void Ins(int x) {
  in[x] = 1;
  auto it = lower_bound(vec.begin(), vec.end(), x);
  int l = *prev(it), r = *it;
  UpdSeg(l, x, 1);
  UpdSeg(x, r, 1);
  if(can[x]) Upd(x, 1);
  UpdSeg(l, r, -1);
  vec.insert(it, x);
}

void Era(int x) {
  in[x] = 0;
  auto it = lower_bound(vec.begin(), vec.end(), x);
  int l = *prev(it), r = *next(it);
  UpdSeg(l, r, 1);
  UpdSeg(l, x, -1);
  UpdSeg(x, r, -1);
  if(can[x]) Upd(x, -1);
  vec.erase(it);
}

void SA() {
  const double T = 1, dlt = 0.9999, eps = 1e-9;
  int run = 0;
  for(double t = T; t > eps; t *= dlt) {
    run++;
    int p = vec[rnd(1, m)], q;
    do q = New(); while(in[q]) ;
    int tmp = mx;
    Ins(q), Era(p);
    if(Chkmin(mn, mx)) ans = vec;
    if((mx > tmp) && (exp((tmp - mx) / t) < rd())) Ins(p), Era(q);
  }
  cerr << "run = " << run << ": " << mn << " " << mx << endl;
}

int main() {
  freopen("1.in", "r", stdin);
  freopen("1.out", "w", stdout);
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> type >> n >> m >> V;
  if(type == 2) {
    for(int i = 1; i <= n; i++) can[i] = IsPrime(i);
  } else if(type == 3) {
    for(int i = 1; i <= n; i++) can[i] = 1;
  } else if(type == 4) {
    int c, x;
    cin >> c;
    while(c--) cin >> x, can[x] = 1;
    for(int i = 1; i <= n; i++) cerr << can[i];
    cerr << endl;
  }
  for(int i = 1; i <= V; i++) {
    for(int j = 1; j <= V; j++) {
      g[i][j] = __gcd(i, j);
    }
  }
  for(int i = 1; i <= V; i++) {
    if(D(i) >= 4) all.push_back(i);
  }
  set <int> st {0, V + 1};
  while(st.size() - 2 < m) st.insert(New());
  vec = vector<int> (st.begin(), st.end());
  for(int i = 1; i <= m; i++) {
    in[vec[i]] = 1;
  }
  for(auto it = st.begin(); it != prev(st.end()); it++) {
    int l = *it, r = *next(it);
    if(l && can[l]) Upd(l, 1);
    for(int i = l + 1; i < r; i++) {
      if(can[i]) Upd(g[l][i] + g[r][i] - g[l][r], 1);
    }
  }
  cerr << "mx = " << mx << endl;
  mn = mx, ans = vec;
  for(int i = 0; i < 40; i++) SA();
  cout << "mn = " << mn << "\n";
  cout << "{";
  for(int i = 1; i <= m; i++) {
    cout << ans[i];
    if(i != m) cout << ", ";
  }
  cout << "}\n";
  
  map <int, int> mp;
  st = set<int> (ans.begin(), ans.end());
  for(auto it = st.begin(); it != prev(st.end()); it++) {
    int l = *it, r = *next(it);
    if(l && can[l]) mp[l]++;
    cerr << l << " " << r << ": " << g[l][r] << endl;
    for(int i = l + 1; i < r; i++) {
      if(can[i]) mp[g[l][i] + g[r][i] - g[l][r]]++;
    }
  }
  for(auto [v, c] : mp) cerr << v << " " << c << endl;
  return 0;
}
posted @ 2026-08-07 22:28  CJzdc  阅读(40)  评论(4)    收藏  举报