題目
正解
據說是一道論文題……
論文:2018集訓隊論文高睿泉《淺談保序迴歸問題》
保序迴歸問題:
有一個正整數,給出一個有向圖,點有權值,需要調整的值使得滿足有向無環圖的偏序關係。調整的代價爲前後的差的次方乘,求最小的代價。
形式化地說:給每個點賦一個新的權值,使得每條邊滿足,求的最小值。
這樣的問題記作
其實這種問題之前已經做過一次了:jzoj6734. 【2020.06.18省選模擬】T2 航行
沒錯就是比賽的前天做到的題
套路做法:整體二分,強制每個權值只能選擇或,跑最大(小)權閉合子圖,選的點最終的權值,選的點最終的權值,分開來遞歸求解。
之前做的那一道題的限制關係比較優美,所以可以DP處理。
最大(小)權閉合子圖:
“閉合子圖”就是某個點集,滿足對於任意,對於任意都有。用人話說就是從中的每個點開始遍歷,
能夠遍歷到的每個點都在中。每個點上有個權值,要選出一個閉合子圖使得點權最大。
一般套路:網絡流,建個新圖。對於每個點,如果就連邊,如果就連邊。原圖中的每一條邊都在新圖中對應地連,容量無窮。
答案爲
理解:如果不考慮限制,則貪心地選所有正權點是最優的。加入限制,對於一個點,如果它不選,則所有,滿足從開始遍歷可以走到,這些都不能選。顯然所有滿足這個條件是充分必要條件。
放在圖中來看:割掉邊,即選,或者割掉邊,即不選。
具體來說,求解最大(小)權閉合子圖的時候,如下處理:
將權值選記作“不選”,將權值選記作“選”。
對於點,它的權值設爲(爲變爲時的代價)。爲什麼這麼設,因爲本該跑最小權閉合子圖,取個負號就變成最大權閉合子圖了。當然也可以不這樣設。
如果某個點“選”,那麼它能遍歷到的所有點都必須“選”。由此得出限制關係,建圖。
跑最大權閉合子圖。
答案即
具體方案看集或集即可。
這題的正解大概可以分爲兩個部分:求出限制關係和求解保序迴歸問題。
後者上面已經討論過了,就講述一下前者。
只考慮帶來的限制,同理。
顯然一個禮品集合就是一個線性基。假如有另一個線性基,它可以通過從開始,依次替換線性基中的向量得到,並且保證替換的過程中一直都是線性基。
表示不會證。(具體證明好像要用到擬陣之類的)。
假設知道了上面的結論是對的,那麼顯然我們只需考慮被替換了一個元素的情形。於是,對於每個數,找到能替換哪些元素,欽定它們都要小於等於。
不難發現能替換哪些元素,等價於能被表示成中的哪些元素異或起來。
這應該是線性基的一個基本操作吧。建線性基的時候,用個bitset
來表示線性基中的某個數是原來的哪幾個數異或過來。查詢的時候,將異或的線性基中的幾個數的bitset
異或起來。詳見代碼。
這題比較小,直接開整型壓位即可。
(有更優美的操作請路過的大佬指教)
代碼
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <climits>
#define N 1005
#define M 64
#define ll long long
#define ull unsigned long long
#define INF LLONG_MAX
int n,m;
ull c[N];
int v[N],y[N];
int a[M],b[M];
struct edge{
int u,v;
} ed[N*M*2];
int cnt;
void getGraph(int a[],int flag){
static ull s[M],p[M];
memset(s,0,sizeof s);
memset(p,0,sizeof p);
for (int i=0;i<m;++i){
ull x=c[a[i]],t=1ull<<i;
for (int j=0;j<64;++j)
if (x>>j&1){
if (s[j]){
x^=s[j];
t^=p[j];
}
else{
s[j]=x;
p[j]=t;
break;
}
}
}
for (int i=1;i<=n;++i){
ull x=c[i],t=0;
for (int j=0;j<64 && x;++j)
if (x>>j&1){
x^=s[j];
t^=p[j];
}
for (int j=0;j<m;++j)
if (t>>j&1 && a[j]!=i)
ed[cnt++]=(flag==1?(edge){a[j],i}:(edge){i,a[j]});
}
}
struct EDGE{
int to;
ll c;
EDGE *las;
} e[(N*M*2+N)*2];
int ne;
EDGE *last[N];
int S,T;
void link(int u,int v,ll c){
e[ne]={v,c,last[u]};
last[u]=e+ne++;
}
#define rev(ei) (e+(int((ei)-e)^1))
int dis[N],gap[N],BZ;
EDGE *cur[N];
ll dfs(int x,ll s){
if (x==T)
return s;
ll have=0;
for (EDGE *ei=cur[x];ei;ei=ei->las){
cur[x]=ei;
if (ei->c && dis[ei->to]+1==dis[x]){
ll t=dfs(ei->to,min(s-have,ei->c));
ei->c-=t,rev(ei)->c+=t,have+=t;
if (have==s)
return s;
}
}
cur[x]=last[x];
if (!--gap[dis[x]])
BZ=0;
++dis[x];
++gap[dis[x]];
return have;
}
void flow(){
BZ=1;
while (BZ)
dfs(S,INF);
}
int p[N];
ll need(int x,int y){return (ll)(v[x]-y)*(v[x]-y);}
int vis[N],bz;
void find(int x){
vis[x]=bz;
for (EDGE *ei=last[x];ei;ei=ei->las)
if (ei->c && vis[ei->to]!=bz)
find(ei->to);
}
void divide(int lv,int rv,int p[],int n,edge ed[],int m){
static int tmpp[N];
static edge tmped[N*M*2];
if (n==0)
return;
if (lv==rv){
for (int i=0;i<n;++i)
y[p[i]]=lv;
return;
}
int mid=lv+rv>>1;
ne=0;
for (int i=0;i<n;++i)
last[p[i]]=0;
last[S]=last[T]=0;
for (int i=0;i<m;++i){
link(ed[i].u,ed[i].v,INF);
link(ed[i].v,ed[i].u,0);
}
for (int i=0;i<n;++i){
ll w=-(need(p[i],mid+1)-need(p[i],mid));
if (w>0)
link(S,p[i],w),link(p[i],S,0);
else if (w<0)
link(p[i],T,-w),link(T,p[i],0);
}
dis[S]=dis[T]=0;
for (int i=0;i<n;++i)
dis[p[i]]=0;
gap[0]=n+2;
flow();
gap[dis[S]]--,gap[dis[T]]--;
for (int i=0;i<n;++i)
gap[dis[p[i]]]--;
++bz,find(S);
int p0=0,p1=n-1;
for (int i=0;i<n;++i)
if (vis[p[i]]!=bz)
tmpp[p0++]=p[i];
else
tmpp[p1--]=p[i];
memcpy(p,tmpp,sizeof(int)*n);
int e0=0,e1=m-1;
for (int i=0;i<m;++i)
if (vis[ed[i].u]!=bz && vis[ed[i].v]!=bz)
tmped[e0++]=ed[i];
else if (vis[ed[i].u]==bz && vis[ed[i].v]==bz)
tmped[e1--]=ed[i];
memcpy(ed,tmped,sizeof(edge)*m);
divide(lv,mid,p,p0,ed,e0);
divide(mid+1,rv,p+p1+1,n-1-p1,ed+e1+1,m-1-e1);
}
int main(){
freopen("shop.in","r",stdin);
freopen("shop.out","w",stdout);
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]);
for (int i=0;i<m;++i)
scanf("%d",&b[i]);
getGraph(a,1),getGraph(b,-1);
for (int i=0;i<n;++i)
p[i]=i+1;
S=n+1,T=n+2;
divide(0,1000000,p,n,ed,cnt);
ll ans=0;
for (int i=1;i<=n;++i)
ans+=need(i,y[i]);
printf("%lld\n",ans);
return 0;
}