loj3301.「联合省选 2020 A」魔法商店
论文题,我连暴力都不会打
这个异或显然是线性基,后面的部分如果大家知道的话就是保序回归的板子(参考高睿泉 2018 年集训队论文《浅谈保序回归问题》,可以去这里自取)。
保序回归问题的模型是给出一个点集和偏序关系,每个点有权值 \(w_i\),求一组 \(f_i\),使得如果 \(w_i<w_j\) 有 \(f_i<f_j\),最小化 \(\sum_{i=1}^n|f_i-w_i|^p\)。
偏序关系相当于一个 DAG,然后我们整体二分,二分每个点的取值。考虑每个点取左半段还是右半段更优,不难发现如果一个点选右半段,那么 DAG 上这个点能走到的点都必须选右半段,这就是个最小权闭合子图,网络流即可。
解决了保序回归之后考虑怎样建图。直接建出线性基,然后枚举所有元素,每次删掉一个,再枚举全部的物品,把它们插入线性基,如果能插入说明这个元素也可以成为集合中的一部分,如果是 \(A\) 集合就删去的物品向当前物品连边,否则反过来,这样就建出图了。
细节巨大多,写起来很像是个三合一题目(
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
#define int long long
struct edge
{
int nxt,to,weight;
}e[1000001<<1];
int n,m,tot,h[1005],s,t,dep[1005],cur[1005],q[1005],tmp[1005][2],w[1005],ans[1005],a[65],b[65],sum;
unsigned long long p[64],c[1005];
bool vis[1005];
int num[1005];
vector<int> v[1005];
inline void init(int maxn)
{
s=maxn+1;
t=s+1;
tot=1;
}
inline void add(int x,int y,int w)
{
e[++tot].nxt=h[x];
h[x]=tot;
e[tot].to=y;
e[tot].weight=w;
}
inline bool bfs()
{
for(register int i=0;i<=t;++i)
{
cur[i]=h[i];
dep[i]=1ll<<60;
vis[i]=0;
}
queue<int> q;
q.push(s);
dep[s]=0;
while(!q.empty())
{
int k=q.front();
q.pop();
vis[k]=0;
for(register int i=h[k];i;i=e[i].nxt)
if(e[i].weight&&dep[e[i].to]>dep[k]+1)
{
dep[e[i].to]=dep[k]+1;
if(!vis[e[i].to])
{
vis[e[i].to]=1;
q.push(e[i].to);
}
}
}
return dep[t]^dep[0];
}
int dfs(int k,int f)
{
if(k==t)
return f;
int r=0,used=0;
for(register int i=cur[k];i;i=e[i].nxt)
{
cur[k]=i;
if(e[i].weight&&dep[e[i].to]==dep[k]+1)
if((r=dfs(e[i].to,min(e[i].weight,f-used))))
{
e[i].weight-=r;
e[i^1].weight+=r;
used+=r;
if(f==used)
break;
}
}
return used;
}
inline void dinic()
{
while(bfs())
dfs(s,1ll<<60);
}
void solve(int l,int r,int ql,int qr)
{
//printf("%lld %lld %lld %lld\n",l,r,ql,qr);
if(ql>qr||l>r)
return;
if(l==r)
{
for(register int i=ql;i<=qr;++i)
ans[q[i]]=l;
return;
}
if(r-l==1)
{
init(qr-ql+1);
for(register int i=ql;i<=qr;++i)
num[q[i]]=i-ql+1;
for(register int i=ql;i<=qr;++i)
{
if(((w[q[i]]-l)<<1)-1>0)
{
add(s,num[q[i]],((w[q[i]]-l)<<1)-1);
add(num[q[i]],s,0);
}
else
{
add(num[q[i]],t,-(((w[q[i]]-l)<<1)-1));
add(t,num[q[i]],0);
}
for(register int j=0;j<(int)v[q[i]].size();++j)
if(num[v[q[i]][j]])
{
add(num[q[i]],num[v[q[i]][j]],1ll<<60);
add(num[v[q[i]][j]],num[q[i]],0);
}
}
dinic();
for(register int i=ql;i<=qr;++i)
{
ans[q[i]]=dep[num[q[i]]]==1ll<<60? l:r;
num[q[i]]=0;
}
for(register int i=1;i<=t;++i)
h[i]=0;
return;
}
int mid=(l+r)>>1;
init(qr-ql+1);
for(register int i=ql;i<=qr;++i)
num[q[i]]=i-ql+1;
for(register int i=ql;i<=qr;++i)
{
if(w[q[i]]>mid)
{
add(s,num[q[i]],(w[q[i]]-mid)<<1);
add(num[q[i]],s,0);
}
else
{
add(num[q[i]],t,(mid-w[q[i]])<<1);
add(t,num[q[i]],0);
}
for(register int j=0;j<(int)v[q[i]].size();++j)
if(num[v[q[i]][j]])
{
add(num[q[i]],num[v[q[i]][j]],1ll<<60);
add(num[v[q[i]][j]],num[q[i]],0);
}
}
dinic();
int cnt1=0,cnt2=0;
for(register int i=ql;i<=qr;++i)
{
if(dep[num[q[i]]]==1ll<<60)
tmp[++cnt1][0]=q[i];
else
tmp[++cnt2][1]=q[i];
num[q[i]]=0;
}
for(register int i=1;i<=cnt1;++i)
q[ql+i-1]=tmp[i][0];
for(register int i=1;i<=cnt2;++i)
q[ql+cnt1+i-1]=tmp[i][1];
for(register int i=1;i<=t;++i)
h[i]=0;
solve(l,mid,ql,ql+cnt1-1);
solve(mid,r,ql+cnt1,qr);
}
inline void clear()
{
for(register int i=0;i<64;++i)
p[i]=0;
}
inline int insert(unsigned long long x)
{
for(register int i=63;~i;--i)
if(x&(1ull<<i))
{
if(!p[i])
{
p[i]=x;
return i;
}
else
x^=p[i];
}
return -1;
}
signed main()
{
scanf("%lld%lld",&n,&m);
for(register int i=1;i<=n;++i)
scanf("%llu",&c[i]);
for(register int i=1;i<=n;++i)
scanf("%lld",&w[i]);
for(register int i=1;i<=m;++i)
scanf("%lld",&a[i]);
for(register int i=1;i<=m;++i)
scanf("%lld",&b[i]);
for(register int i=1;i<=m;++i)
{
clear();
for(register int j=1;j<=m;++j)
if(j!=i)
insert(c[a[j]]);
for(register int j=1;j<=n;++j)
if(j!=a[i])
{
int pos=insert(c[j]);
if(~pos)
{
v[a[i]].push_back(j);
p[pos]=0;
}
}
}
for(register int i=1;i<=m;++i)
{
clear();
for(register int j=1;j<=m;++j)
if(j!=i)
insert(c[b[j]]);
for(register int j=1;j<=n;++j)
if(j!=b[i])
{
int pos=insert(c[j]);
if(~pos)
{
v[j].push_back(b[i]);
p[pos]=0;
}
}
}
for(register int i=1;i<=n;++i)
q[i]=i;
solve(0,1e6,1,n);
for(register int i=1;i<=n;++i)
sum+=(ans[i]-w[i])*(ans[i]-w[i]);
printf("%lld\n",sum);
return 0;
}