loj 3301 「联合省选 2020 A」魔法商店 - 拟阵 - 保序回归

题目传送门

  传送门

  整个联考的区分度主要在会不会保序回归,次要在常数,有毒。。。

  关于以下使用的定理和结论的证明以及定义,请自行翻 2018 集训队论文。因为我都不会证。

  显然问题是给定一个拟阵 $M$ 和两个基 $I_a$ 以及 $I_b$,定义 $w(I) = \sum_{x\in I} z_x$,要求寻找一组系数 $z_x$,满足 $w(I_a)$ 最小,$w(I_b)$ 最大,并且最小化回归代价 $\sum (z_x - v_x)^2$。 

定理1 如果 $I, J$ 都是 $M$ 的独立集,那么交换图 $D_M(I)$ 中存在关于 $I\backslash J$ 以及 $J \backslash I$ 的完美匹配

  使用强基交换定理归纳。具体过程见论文。

  考虑对于 $I_a -\{x\} + \{y\}$ 得到的新的基,要求满足 $z_x \leqslant z_y$,如果所有都满足这个限制,根据定理1,显然任意一个基都满足 $w(I_a) \leqslant w(I)$。

  然后直接贴保序回归的板子就完事了。

Code

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

#define ll long long
#define ull unsigned ll

const ll llf = (signed ll) (~0u >> 3);

typedef class Edge {
  public:
    int ed, nx;
    ll r;

    Edge(int ed, int nx, ll r) : ed(ed), nx(nx), r(r) {  }
} Edge;

typedef class MapManager {
  public:
    vector<int> h;
    vector<Edge> E;

    MapManager(int n) {
      h.assign(n + 1, -1);
      E.clear();
    }
    ~MapManager() {
      h.clear();
      E.clear();
    }
    void add_edge(int u, int v, ll r) {
      E.emplace_back(v, h[u], r);
      h[u] = (signed) E.size() - 1;  
    }
    Edge& operator [] (int p) {
      assert(p < (signed) E.size());
      return E[p];
    }
} MapManager;

typedef class Network {
  public:
    int S, T;
    vector<int> cur, div;
    MapManager g;

    Network(int S, int T) : S(S), T(T), g(T) {
      div.resize(T + 1);
    }

    void add_edge(int u, int v, ll w) {
      g.add_edge(u, v, w);
      g.add_edge(v, u, 0);
    }

    bool bfs() {
      fill(div.begin(), div.end(), 0);
      queue<int> Q;
      Q.push(S);
      div[S] = 1;
      while (!Q.empty()) {
        int p = Q.front();
        Q.pop();
        for (int i = g.h[p]; ~i; i = g[i].nx) {
          int e = g[i].ed;
          if (g[i].r && !div[e]) {
            div[e] = div[p] + 1;
            Q.push(e);
          }
        }
      }
      return div[T];
    }

    ll dfs(int p, ll mf) {
      if (p == T || !mf) {
        return mf;
      }
      ll flow = 0, f;
      for (int& i = cur[p]; ~i; i = g[i].nx) {
        int e = g[i].ed;
        if (div[e] == div[p] + 1 && (f = dfs(e, min(mf, g[i].r))) > 0) {
          g[i].r -= f;
          g[i ^ 1].r += f;
          flow += f;
          if (!(mf -= f))
            break;
        }
      }
      return flow;
    }

    ll dinic() {
      ll ret = 0;
      while (bfs()) {
        cur = g.h;
        dfs(S, llf);
      }
      return ret;
    }

    vector<bool> get_S() {
      vector<bool> ret (T + 1, false);
      queue<int> Q;
      Q.push(S);
      ret[S] = true;
      while (!Q.empty()) {
        int p = Q.front();
        Q.pop();
        for (int i = g.h[p]; ~i; i = g[i].nx) {
          int e = g[i].ed;
          if (g[i].r && !ret[e]) {
            ret[e] = true;
            Q.push(e);
          } 
        }
      }
      return ret;
    }
} Network;

const int bzmax = 64;

typedef class LinearBasis {
  public:
    ull a[bzmax];

    void clear() {
      memset(a, 0, sizeof(a));
    }
    bool insert(ull x, bool chk = false) {
      for (int i = bzmax; i-- && x; ){
        if ((x >> i) & 1)
          x ^= a[i];
        if ((x >> i) & 1) {
          !chk && (a[i] = x);
          return true;
        }
      }
      return false;
    }
} LinearBasis;

const int N = 1e3 + 3;

int n, m;
int z[N], v[N];
ull a[N];
vector<int> A, B;

void dividing(int l, int r, vector<int> P, vector<pair<int, int>> E, vector<int> org) {
  if (l == r) {
    for (auto x : P) {
      z[org[x]] = l;
    }
    return;
  }
  if (P.empty()) {
    return;
  }
  auto _P = P;
  int n = P.size();
  vector<int> norg(n);
  sort(_P.begin(), _P.end());
#define get(x) (lower_bound(_P.begin(), _P.end(), x) - _P.begin())
  for (auto& x : P) {
    int y = org[x];
    norg[x = get(x)] = y; 
  }
  int mid = (l + r) >> 1;
  Network network (n, n + 1);
  for (auto& e : E) {
    e.first = get(e.first);
    e.second = get(e.second);
    network.add_edge(e.first, e.second, llf);
  }
  for (int x = 0; x < n; x++) {
    int y = norg[x];
    ll dx = ((mid - v[y]) << 1) + 1;
    if (dx < 0) {
      network.add_edge(n, x, -dx);
    } else if (dx > 0) {
      network.add_edge(x, n + 1, dx);
    }
  }
  network.dinic();
  auto inS = network.get_S();
//  for (int i = 0; i < n; i++) {
//    cerr << "(" << norg[i] << ", " << inS[i] << ") "; 
//  }
//  cerr << '\n';
  vector<int> pL, pR;
  vector<pair<int, int>> eL, eR;
  for (int i = 0; i < n; i++) {
    if (inS[i]) {
      pR.push_back(i);
    } else {
      pL.push_back(i);
    }
  }
  for (auto e : E) {
    int u = e.first, v = e.second;
    if (inS[u] && inS[v]) {
      eR.push_back(e);
    } else if (!inS[u] && !inS[v]) {
      eL.push_back(e);
    }
  }
#undef get
  dividing(l, mid, pL, eL, norg);
  dividing(mid + 1, r, pR, eR, norg);
}

LinearBasis lb;
int main() {
  scanf("%d%d", &n, &m);
  for (int i = 1; i <= n; i++) {
    scanf("%llu", a + i);
  }
  int mi = 1e9, mx = -1;
  for (int i = 1; i <= n; i++) {
    scanf("%d", v + i);
    mi = min(mi, v[i]);
    mx = max(mx, v[i]);
  }
  vector<int> p, org;
  vector<pair<int, int>> E0;
  for (int i = 1; i <= n; i++) {
    p.push_back(i);
  }
  org = p;
  org.insert(org.begin(), 0);
  A.resize(m);
  vector<bool> in (n + 1, false);
  for (auto& x : A) {
    scanf("%d", &x);
    in[x] = true;
  }
  for (int i = 0; i < m; i++) {
    lb.clear();
    for (int j = 0; j < m; j++) {
      if (i ^ j) {
        lb.insert(a[A[j]]);
      }
    }
    for (int p = 1; p <= n; p++) {
      if (!in[p] && lb.insert(a[p], true)) {
        E0.emplace_back(A[i], p);
//        cerr << A[i] << " <= " << p << '\n';
      }
    }
  }
  for (auto x : A) {
    in[x] = false;
  }
  B.resize(m);
  for (auto& x : B) {
    scanf("%d", &x);
    in[x] = true;
  }
  for (int i = 0; i < m; i++) {
    lb.clear();
    for (int j = 0; j < m; j++) {
      if (i ^ j) {
        lb.insert(a[B[j]]);
      }
    }
    for (int p = 1; p <= n; p++) {
      if (!in[p] && lb.insert(a[p], true)) {
        E0.emplace_back(p, B[i]);
//        cerr << p << " <= " << B[i] << '\n';
      }
    }
  }
  dividing(mi, mx, p, E0, org);
  ll ans = 0;
  for (int i = 1; i <= n; i++) {
    ans += 1ll * (z[i] - v[i]) * (z[i] - v[i]);
  }
  printf("%lld\n", ans);
  return 0;
}
posted @ 2020-06-24 12:16  阿波罗2003  阅读(586)  评论(0编辑  收藏  举报