[省选联考 2020 A 卷] 魔法商店
一、题目
二、解法
由于 这东西 已经鸽掉了,那么我就写一篇只记录做法的博客吧。
首先讲一下保序回归的一般做法,我们考虑使用整体二分求解 \(f\)(\(f\) 指调整后的价格),设现在 \(f\) 的范围是 \([l,r]\),我们要检测 \([f_i\leq mid]\) 是否为真,称额外限制 \(f_i\in[a,b]\) 的问题为 \(\{a,b\}\) 问题。
我们可以考虑原问题的 \(\{mid,mid+1\}\) 问题,\(f_i\) 是取 \(mid/mid+1\),如果 \(f_i\) 取 \(mid\) 说明最终的 \(f_i\leq mid\),如果 \(f_i\) 取 \(mid+1\) 说明最终的 \(f_i>mid\)
这显然是最大权闭合子图模型,我们先强制所有点选 \(mid\),那么可以计算出调整至 \(mid+1\) 的贡献 \(c\)(但是建图的时候要用 \(-c\),最小转最大),用偏序关系建好图之后跑网络流,如果一个点不和源点联通那么选的是 \(mid\),否则选的是 \(mid+1\),可以根据这个来划分左右。
本题还需要写出偏序关系,考虑最小的充要条件是:线性基的权值小于等于替换一个元素的线性基的权值,这显然是必要条件,可以通过调整法证明其充分性(张成线性空间不变,权值变大)
那么暴力尝试替换就可以得到偏序关系,网络流的题就不说复杂度了。
#include <cstdio>
#include <vector>
#include <iostream>
#include <queue>
using namespace std;
const int M = 1005;
#define int long long
const int inf = 1e18;
#define pb push_back
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,v[M],a[M],b[M],c[M],xs[M];vector<int> g[M];
int tot,S,T,d[M],f[M],cur[M],p[M],q[M],dp[M],L[M],R[M];
struct edge{int v,c,next;}e[M*M];
//line dick
int ins(int x)
{
for(int i=63;i>=0;i--)
{
if(!(x>>i&1)) continue;
if(xs[i]) x^=xs[i];
else {xs[i]=x;return i;}
}
return -1;
}
//network flow
void init(int n)
{
S=0;T=n+1;tot=1;
for(int i=0;i<=T;i++) f[i]=0;
}
void add(int u,int v,int c)
{
e[++tot]=edge{v,c,f[u]},f[u]=tot;
e[++tot]=edge{u,0,f[v]},f[v]=tot;
}
int bfs()
{
for(int i=S;i<=T;i++) d[i]=0;
queue<int> q;d[S]=1;q.push(S);
while(!q.empty())
{
int u=q.front();q.pop();
if(u==T) return 1;
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v;
if(!d[v] && e[i].c>0)
{
d[v]=d[u]+1;
q.push(v);
}
}
}
return 0;
}
int dfs(int u,int ept)
{
if(u==T) return ept;
int flow=0,tmp=0;
for(int &i=cur[u];i;i=e[i].next)
{
int v=e[i].v;
if(d[v]==d[u]+1 && e[i].c>0)
{
tmp=dfs(v,min(ept,e[i].c));
if(!tmp) continue;
flow+=tmp;ept-=tmp;
e[i].c-=tmp;e[i^1].c+=tmp;
if(!ept) break;
}
}
return flow;
}
//divide and conquer
int calc(int x,int y) {return (v[x]-y)*(v[x]-y);}
void cdq(int ql,int qr,int l,int r)
{
if(ql>qr) return ;//no element
if(l==r)
{
for(int i=ql;i<=qr;i++) dp[p[i]]=l;
return ;
}
int mid=(l+r)>>1,tl=0,tr=0;
for(int i=ql;i<=qr;i++) q[p[i]]=i-ql+1;
init(qr-ql+1);
for(int i=ql;i<=qr;i++)
{
int u=p[i],t=calc(u,mid)-calc(u,mid+1);
if(t>0) add(S,q[u],t);
else add(q[u],T,-t);
for(int v:g[u]) if(q[v])
add(q[u],q[v],inf);
}
for(int i=ql;i<=qr;i++) q[p[i]]=0;
while(bfs())
{
for(int i=S;i<=T;i++) cur[i]=f[i];
dfs(S,inf);
}
for(int i=ql;i<=qr;i++)
if(!d[i-ql+1]) L[++tl]=p[i];
else R[++tr]=p[i];
for(int i=1;i<=tl;i++) p[ql+i-1]=L[i];
for(int i=1;i<=tr;i++) p[ql+tl+i-1]=R[i];
cdq(ql,ql+tl-1,l,mid);
cdq(ql+tl,qr,mid+1,r);
}
signed main()
{
n=read();m=read();
for(int i=1;i<=n;i++) c[i]=read();
for(int i=1;i<=n;i++) v[i]=read();
for(int i=1;i<=m;i++) a[i]=read();
for(int i=1;i<=m;i++) b[i]=read();
for(int i=1;i<=m;i++)
{
for(int j=0;j<64;j++) xs[j]=0;
for(int j=1;j<=m;j++)
if(i!=j) ins(c[a[j]]);
for(int j=1;j<=n;j++) if(j!=a[i])
{
int t=ins(c[j]);
if(~t) g[a[i]].pb(j),xs[t]=0;
}
}
for(int i=1;i<=m;i++)
{
for(int j=0;j<64;j++) xs[j]=0;
for(int j=1;j<=m;j++)
if(i!=j) ins(c[b[j]]);
for(int j=1;j<=n;j++) if(j!=b[i])
{
int t=ins(c[j]);
if(~t) g[j].pb(b[i]),xs[t]=0;
}
}
for(int i=1;i<=n;i++) p[i]=i;
cdq(1,n,0,1e6);int ans=0;
for(int i=1;i<=n;i++) ans+=calc(i,dp[i]);
printf("%lld\n",ans);
}