参考 2018 年高睿泉的集训队论文《浅谈保序回归问题》。
保序回归问题
定义
保序回归问题是指,对于一个正整数
我们把上述问题称作
举个简单的例子,P4331 这道题就可以转化为全序关系(链)上的
L_p 均值与其性质
我们定义
整体二分
应用整体二分的思想,可以得到一个求问题的近似解的算法。
我们在原问题的基础上加入一些限制。定义
尝试构造另一个问题。设开区间
若
solve(U,a,b) 的一组最优解为\tilde z ,且满足\tilde z_{i}\notin (a,b),\forall i\in U 。则存在solve(U,l,r) 的一组最优解z ,使得z_i\leq a 当且仅当\tilde z_i=a 。
证明:利用反证法。由于上面的性质,显然
-
若
k\geq b ,则可以发现对于一个 B 类点,它在区间(-\infty,k] 内的回归代价递减,而在区间[k,+\infty) 内的回归代价递增。设t 是满足z_i\leq a 的 B 类点中最大的z_i ,那么我们把所有z_i=t 的 B 类点移动到b 位置处,它们的回归代价变小,而所有点的相对大小关系不变。因此与z 是solve(U,l,r) 的一组最优解的假设矛盾。 - 若
k\leq a ,则z_i\leq a 的 B 类点i 显然不会存在 A 类点j 满足z_i\leq z_j 这样的偏序关系。如果这样的 B 类点只有z_i=t 这一种取值,则其所有y_i\leq a ,所以将它们改为 A 类点回归代价会减小,且所有点的相对大小仍不发生改变。因此与\tilde z 是solve(U,a,b) 的一组最优解的假设矛盾。对于这些 B 类点的z_i 有多种取值的情况,则考虑设点集V=\{i\mid z_i<t,\tilde z_i=b\} ,其L_p 均值为k' ,如果k'\leq t 则可以将V 中z_i 最大的点调整为 A 类点得到solve(U,a,b) 的一组更优解;否则类似第一种情况,将V 中的一些z_i 调整到k' 处得到一组不劣于solve(U,l,r) 的答案,如果没有比原答案更优则显然V 中所有z_i 的取值相同,重复上述过程即可导出矛盾。
因为
回到原问题,考虑找到满足性质的区间
由于
本题题解
题目中要求了大小关系的子集都是给定的
然后有个问题就是这题要求修改后的价格为整数,可以这样处理:当
代码
#include <cstdio>
#include <queue>
using namespace std;
typedef unsigned long long ull;
const int MAXN=1005;
const int INF=1E9;
namespace MF
{
struct Edge
{
int from, to, cap, flow;
Edge(int u, int v, int c, int f):
from(u), to(v), cap(c), flow(f) {}
};
int n, s, t, flow;
vector<Edge> edges;
vector<int> g[MAXN];
int h[MAXN], cur[MAXN];
void init(int v, int a, int b)
{
n=v, s=a, t=b, flow=0;
edges.clear();
for (int i=1; i<=n; i++) g[i].clear();
}
void addEdge(int from, int to, int cap)
{
edges.push_back(Edge(from, to, cap, 0));
edges.push_back(Edge(to, from, 0, 0));
g[from].push_back(edges.size()-2);
g[to].push_back(edges.size()-1);
}
bool bfs()
{
queue<int> q;
for (int i=1; i<=n; i++) cur[i]=h[i]=0;
h[s]=1, q.push(s);
while (!q.empty())
{
int u=q.front(); q.pop();
for (int i=0; i<g[u].size(); i++)
{
Edge e=edges[g[u][i]];
if (e.cap>e.flow&&!h[e.to])
h[e.to]=h[u]+1, q.push(e.to);
}
}
return h[t];
}
int dfs(int u, int f)
{
if (u==t) return f;
for (int &i=cur[u]; i<g[u].size(); i++)
{
Edge &e=edges[g[u][i]];
if (e.cap>e.flow&&h[e.to]==h[u]+1)
{
int d=dfs(e.to, min(f, e.cap-e.flow));
if (d>0)
{
e.flow+=d;
edges[g[u][i]^1].flow-=d;
return d;
}
}
}
return 0;
}
void Dinic()
{
while (bfs())
while (int f=dfs(s, INF)) flow+=f;
}
}
namespace XB
{
ull d[64];
void clear()
{
for (int i=0; i<64; i++) d[i]=0;
}
int insert(ull x)
{
for (int i=63; i>=0; i--)
if (x&1ll<<i)
if (d[i]) x^=d[i];
else return d[i]=x, i;
return -1;
}
}
ull c[MAXN];
int w[MAXN], a[66], b[66];
int p[MAXN], q[MAXN], t1[MAXN], t2[MAXN];
int f[MAXN];
vector<int> G[MAXN];
void solve(int l, int r, int L, int R)
{
if (l>r) return;
if (R-L==1)
{
MF::init(r-l+3, r-l+2, r-l+3);
for (int i=l; i<=r; i++) q[p[i]]=i-l+1;
for (int i=l; i<=r; i++)
{
int u=p[i], cost=2*(w[u]-L)-1;
if (cost>0) MF::addEdge(MF::s, q[u], cost);
else MF::addEdge(q[u], MF::t, -cost);
for (int v: G[u]) if (q[v])
MF::addEdge(q[u], q[v], INF);
}
for (int i=l; i<=r; i++) q[p[i]]=0;
MF::Dinic();
for (int i=l; i<=r; i++)
if (!MF::h[i-l+1]) f[p[i]]=L;
else f[p[i]]=R;
return;
}
int mid=(L+R)/2;
MF::init(r-l+3, r-l+2, r-l+3);
for (int i=l; i<=r; i++) q[p[i]]=i-l+1;
for (int i=l; i<=r; i++)
{
int u=p[i];
if (mid<w[u]) MF::addEdge(MF::s, q[u], w[u]-mid);
else MF::addEdge(q[u], MF::t, mid-w[u]);
for (int v: G[u]) if (q[v])
MF::addEdge(q[u], q[v], INF);
}
for (int i=l; i<=r; i++) q[p[i]]=0;
MF::Dinic();
int c1=0, c2=0;
for (int i=l; i<=r; i++)
if (!MF::h[i-l+1]) t1[++c1]=p[i];
else t2[++c2]=p[i];
for (int i=1; i<=c1; i++) p[l+i-1]=t1[i];
for (int i=1; i<=c2; i++) p[l+c1+i-1]=t2[i];
solve(l, l+c1-1, L, mid);
solve(l+c1, r, mid, R);
}
int main()
{
// freopen("shop.in", "r", stdin);
// freopen("shop.out", "w", stdout);
int n, m;
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", &w[i]);
for (int i=1; i<=m; i++) scanf("%d", &a[i]);
for (int i=1; i<=m; i++) scanf("%d", &b[i]);
for (int i=1; i<=m; i++)
{
XB::clear();
for (int j=1; j<=m; j++)
if (j!=i) XB::insert(c[a[j]]);
for (int j=1; j<=n; j++)
if (j!=a[i])
{
int t=XB::insert(c[j]);
if (~t) G[a[i]].push_back(j), XB::d[t]=0;
}
}
for (int i=1; i<=m; i++)
{
XB::clear();
for (int j=1; j<=m; j++)
if (j!=i) XB::insert(c[b[j]]);
for (int j=1; j<=n; j++)
if (j!=b[i])
{
int t=XB::insert(c[j]);
if (~t) G[j].push_back(b[i]), XB::d[t]=0;
}
}
for (int i=1; i<=n; i++) p[i]=i;
solve(1, n, 0, 1E6);
ull ans=0;
for (int i=1; i<=n; i++) ans+=1ll*(f[i]-w[i])*(f[i]-w[i]);
printf("%llu\n", ans);
return 0;
}