【luogu P6621】【LOJ 3301】魔法商店(線性基)(保序迴歸)
阿新 • • 發佈:2022-04-14
魔法商店
題目連結:luogu P6621 / LOJ 3301
題目大意
給你 n 個物品,每個有魅力值和價格。
然後一組合法的方案定義為數量最多的一個物品集,使得每個非空子集魅力值的異或和都非 0。
然後你可以修改物品的價格,費用是價格差的平方。
然後給你兩個合法方案,要你用最小的費用使得它們在所有合法方案中一個價格和最大,一個最小。
思路
首先看到那個合法方案就是每個都能放進線性基裡面。
那最大最小可以分開看看。
然後發現你枚舉出所有合法方案不太行,考慮先是最小的怎麼做。
那我們考慮能否把最小的集合變化一下得到別的。
那如果這個集合 \(A\) 裡面一個子集的異或和是 \(k\),那 \(k\)
那集合 \(A\) 還是最小的,那我們就其實可以通過這個替代列出一些關係 \(v_x\leqslant v_y\)。
對 \(A\) 來說,就是它自己裡面的 \(v\) 要小於等於替換的 \(k\) 的 \(v\) 值。
至於 \(B\),就是大於等於。
然後你會發現這個關係已經充要了。
那我們就可以考慮這些偏序關係怎麼滿足:
保序迴歸!
然後因為第一次做網路流的保序迴歸,就說說怎麼做。
首先也是整體二分,然後每次有一個點集和一個答案的範圍。
然後首先假設答案不要求整數,那它就是一個實數,你總不能遞迴下去,於是考慮這麼一個方法:
\(mid\) 和一個位置 \(mid+x\)
那我們就可以把這個作為權值,至於為啥是網路流做,你會發現你因為限制條件,你一個點如果要選前面的那能到它的點都要選前面。
那它其實就是最大權閉合圖的感覺,那就是網路流模板啦。
(具體一點你要最小的權值,然後你每個的權值就是左邊的減右邊的,然後搞最小割)
然後至於答案要是整數,那我們就不能完全用上面的方法搞,因為你 \(L+1=R\) 的時候你小的位置 \(L+x\) 不能代表 \(R\),所以就直接兩個用 \(L,R\),然後就選 \(L,R\) 給它即可。
然後最後帶進去求每個費用加起來即可。
程式碼
#include<queue>
#include<cstdio>
#include<vector>
#include<iostream>
#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
const int N = 1000 + 10;
const int M = 64;
int n, m, v[N], a[M], b[M], ans[N];
bool in1[N], in2[N];
bool lnk[N][N];//[i][j] -> i<=j
ull c[N];
vector <int> d;
struct XXJ {
ull f[M], hav[M];
void clear() {
for (int i = 0; i < M; i++) f[i] = hav[i] = 0;
}
void insert(ull x, ull pl) {
for (int i = M - 1; i >= 0; i--)
if ((x >> i) & 1) {
if (f[i]) x ^= f[i], pl ^= hav[i];
else {f[i] = x; hav[i] = pl; break;}
}
}
ull get_id(ull x) {
ull id = 0;
for (int i = M - 1; i >= 0; i--)
if ((x >> i) & 1) x ^= f[i], id ^= hav[i];
return id;
}
}H;
struct Map_Flow {
struct node {
ll x;
int to, nxt, op;
}e[N * N * 2];
int le[N], KK, tot, S, T, deg[N], lee[N];
queue <int> q;
void Init(int siz) {
tot = siz; S = ++tot; T = ++tot;
for (int i = 1; i <= tot; i++) le[i] = 0; KK = 0;
}
void add(int x, int y, ll z) {
e[++KK] = (node){z, y, le[x], KK + 1}; le[x] = KK;
e[++KK] = (node){0, x, le[y], KK - 1}; le[y] = KK;
}
bool bfs() {
while (!q.empty()) q.pop();
for (int i = 1; i <= tot; i++) deg[i] = 0, lee[i] = le[i];
q.push(S); deg[S] = 1;
while (!q.empty()) {
int now = q.front(); q.pop();
for (int i = le[now]; i; i = e[i].nxt)
if (e[i].x && !deg[e[i].to]) {
deg[e[i].to] = deg[now] + 1;
if (e[i].to == T) return 1;
q.push(e[i].to);
}
}
return 0;
}
ll dfs(int now, ll sum) {
if (now == T) return sum;
ll go = 0;
for (int &i = lee[now]; i; i = e[i].nxt)
if (e[i].x && deg[e[i].to] == deg[now] + 1) {
ll this_go = dfs(e[i].to, min(sum - go, e[i].x));
if (this_go) {
e[i].x -= this_go; e[e[i].op].x += this_go;
go += this_go; if (go == sum) return go;
}
}
return go;
}
void dinic() {
while (bfs())
dfs(S, INF);
}
}G;
//(x^p)'=px^{p-1}
//(x^2)'=2x
void slove(vector <int> d, int L, int R) {
if (d.empty()) return ;
if (L >= R) return ;
int mid = (L + R) >> 1; G.Init(d.size());
for (int i = 0; i < d.size(); i++) {
int x = d[i];
ll va = (L + 1 == R) ? 1ll * (v[x] - R) * (v[x] - R) - 1ll * (v[x] - L) * (v[x] - L) : 2ll * (mid - v[x]);
if (va < 0) G.add(G.S, i + 1, -va);
else G.add(i + 1, G.T, va);
}
for (int i = 0; i < d.size(); i++)
for (int j = 0; j < d.size(); j++)
if (lnk[d[i]][d[j]]) G.add(i + 1, j + 1, INF);
G.dinic();
if (L + 1 == R) {
for (int i = 0; i < d.size(); i++)
if (G.deg[i + 1]) ans[d[i]] = R;
else ans[d[i]] = L;
return ;
}
vector <int> dl, dr;
for (int i = 0; i < d.size(); i++)
if (G.deg[i + 1]) dr.push_back(d[i]);
else dl.push_back(d[i]);
slove(dl, L, mid); slove(dr, mid, R);
}
int main() {
// freopen("read.txt", "r", stdin);
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%llu", &c[i]);
for (int i = 1; i <= n; i++) scanf("%d", &v[i]);
for (int i = 0; i < m; i++) scanf("%d", &a[i]), in1[a[i]] = 1;
for (int i = 0; i < m; i++) scanf("%d", &b[i]), in2[b[i]] = 1;
H.clear(); for (int i = 0; i < m; i++) H.insert(c[a[i]], 1ull << i);
for (int i = 1; i <= n; i++) if (!in1[i]) {
ull id = H.get_id(c[i]);
for (int j = 0; j < m; j++) if ((id >> j) & 1)
lnk[a[j]][i] = 1;
}
H.clear(); for (int i = 0; i < m; i++) H.insert(c[b[i]], 1ull << i);
for (int i = 1; i <= n; i++) if (!in2[i]) {
ull id = H.get_id(c[i]);
for (int j = 0; j < m; j++) if ((id >> j) & 1)
lnk[i][b[j]] = 1;
}
for (int i = 1; i <= n; i++) d.push_back(i);
slove(d, 0, 1e6);
ll answer = 0;
for (int i = 1; i <= n; i++) answer += 1ll * (v[i] - ans[i]) * (v[i] - ans[i]);
printf("%lld", answer);
return 0;
}