1. 程式人生 > 其它 >《淺談保序迴歸問題》學習筆記

《淺談保序迴歸問題》學習筆記

《淺談保序迴歸問題》學習筆記

《淺談保序迴歸問題》學習筆記

保序迴歸問題

偏序關係

定義二元關係 \(\preceq\) 為集合 \(S\) 上的偏序關係,滿足:

  • 自反性:\(\forall x\in S,x\preceq x\)
  • 反對稱性:\(\forall x,y\in S,x\preceq y\land y\preceq x\Rightarrow x=y\)
  • 傳遞性:\(\forall x,y,z\in S,x\preceq y\land y\preceq z\Rightarrow x\preceq z\)

\(L_p\) 問題

問題: 給定 DAG \(G=(V=\{v_i\}_{i=1}^n,E)\)、代價函式 \(\{(y_i,w_i)\}_{i=1}^n\)

。定義偏序關係 \(v_i\preceq v_j\) 當且僅當 \(G\) 中存在 \(v_i\)\(v_j\) 的路徑。

求出

\[\min\{\sum_{i=1}^nw_i|f_i-y_i|^p |\forall v_{i}\preceq v_j,f_i\leq f_j\},p<\infty\\ \min\{\max_{i=1}^nw_i|f_i-y_i| |\forall v_{i}\preceq v_j,f_i\leq f_j\},p=\infty \]

序列 \(z\) 向集合 \(S=\{a,b\}\) 取整

\(\{z_i\}_{i=1}^n\gets \{\min\{b,\max\{a,z_i\}\}\}_{i=1}^n\)

點集 \(U\)\(L_p\) 均值

使得 \(\sum_{v_i\in U}w_i|y_i-k|^p(p<\infty)\)\(\max_{v_i\in U}w_i|y_i-k|(p=\infty)\) 取到最小值的 \(k\)

一般問題的解法

\(L_p\)\(S=\{a,b\}(a<b)\) 問題

\(L_p\) 問題中增加 \(\forall 1\leq i\leq n,a\leq f_i\leq b\) 的限制。

\(L_1\) 問題解法

引理:\(\forall 1\leq i\leq n,y_i\notin (a,b)\) 且存在最優解序列 \(\{z_i\}_{i=1}^n,s.t.\forall 1\leq i\leq n,z_i\notin (a,b)\)

,那麼對於 \(S=\{a,b\}\) 問題的最優解 \(z^S\),都存在一個原問題的最優解 \(z\),滿足 \(z\)\(S\) 取整後得到 \(z^S\)

對於 \(L_1\) 問題的最優解 \(z\)\(z\) 中的元素一定在 \(y\) 構成的集合 \(\{Y_i\}_{i=1}^k(\forall 1\leq i<k,Y_i<Y_{i+1})\) 中。於是可以進行整體二分:當二分到 \(Y_{[l,r]}\) 時,求出 \(S=\{Y_{mid},Y_{mid+1}\}\) 問題的最優解,然後通過這一組解將此區間內的節點重新劃分至 \(Y_{[l,mid]}\)\(Y_{[mid+1,r]}\) 遞迴求解。

\(L_p(1<p<\infty)\) 問題解法

引理: \(\forall 1<p<\infty,U\subset V\)\(U\)\(L_p\) 均值是唯一的。

引理:\(\forall U\subset V\)\(U\)\(L_p\) 均值不在 \((a,b)(a<b)\) 中且存在最優解 \(\{z_i\}_{i=1}^n,s.t.\forall 1\leq i\leq n,z_i\notin (a,b)\)。那麼對於代價函式為 \((y',w')\)\(L_1\) 問題的最優解 \(\tilde{z}\),存在原問題的最優解 \(\{z_i\}_{i=1}^n,s.t.\forall 1\leq i\leq n,z_i\leq a\Leftrightarrow \tilde{z}_i=0\)。其中:

\[(y'_i,w'_i)=\left\{\begin{aligned}(0,w_i[(b-y_i)^p-(a-y_i)^p]),y_i\leq a\\(1,w_i[(y_i-a)^p-(y_i-b)^p]),y_i>a\end{aligned}\right. \]

類似於 \(L_1\) 問題,可以進行在實數上的整體二分:當二分到 \([l,r]\) 時,選定一個極小的正實數 \(\epsilon\),那麼 \((a,b)=(mid,mid+\epsilon)\) 時滿足上述條件,求出 \(\tilde{z}\),進行遞迴求解(不影響結果地,可以將 \(w'\) 同時除以 \(\epsilon\),變為原表示式在 \(mid\) 處的導數)。

\(L_1\)\(S\) 問題解法

對於一般 DAG,可以抽象為最小權閉合子圖問題,用網路流解決。

對於樹/仙人掌/多維偏序,可以根據特殊性質進行 dp 求解。

\(L_\infty\) 問題的解法與擴充套件

二分法

二分答案+DAG 上 dp。

問題拓展

問題: 在滿足 \(L_\infty\) 的限制下,對於每個 \(1\leq k\leq n\) 求出 \(\min\{\max_{i=1}^kw_i|f_i-y_i|\}\)。另外,增加條件 \(\forall 1\leq i<j\leq n,v_i\preceq v_j\)

定義 \(err(v_i,r)=w_i|r-y_i|\)\(mean(v_i,v_j)=\frac{w_iy_i+w_jy_j}{w_i+w_j}\)

\(v_i\preceq v_j\land y_i>y_j\) 時,\(mean\_err(v_i,v_j)=\max\{err(v_i,mean(v_i,v_j)),err(v_j,mean(v_i,v_j))\}\);否則 \(mean\_err(v_i,v_j)=0\)

引理: 對於任意 \(L_\infty\) 問題,整張圖的答案為 \(\max\{mean\_err(v_i,v_j\}\)

\(pre(v_i)=\max\{mean\_err(v_j,v_i)|v_j\preceq v_i\}\)

那麼對於任意 DAG 可以 \(O(n^2)\) 求出 \(pre\),進而求出答案。對於新增加的條件,可以通過維護凸包以做到 \(O(n\log n)\)

例題

[省選聯考 2020 A 卷] 魔法商店

簡要題意

給定一個非負整數集合,每個元素有一個權值,你可以修改每個元素的權值各一次,代價為修改前後權值差的平方。指定該集合的兩組不同線性基 \(A,B\),要求 \(A\) 為所有線性基中權值和最小的,\(B\) 為最大的。求出最小代價。

題解

要求等價於 \(A\) 中每個元素的權值都要小於等於能替換該元素的其他元素的權值,\(B\) 反之。建立出偏序關係然後跑 \(L_2\) 問題即可。

code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#define inf 0x3f3f3f3f
#define ull unsigned long long
#define N 1005
#define M 70

namespace XB{
	#define W 64
	ull d[W];
	inline void cl(){
		for(int i=0;i<W;i++)
			d[i]=0;
	}
	inline void ins(ull x){
		for(int i=W-1;i>=0;i--)
			if((x>>i)&1){
				if(!d[i]){
					d[i]=x;
					break;
				}
				x^=d[i];
			}
	}
	inline bool chk(ull x){
		for(int i=W-1;i>=0;i--)
			if((x>>i)&1)
				x^=d[i];
		return x>0;
	}
}

namespace MF{
	int n,s,t;
	
	int hd[N],_hd;
	struct edge{
		int v,f,nxt;
	}e[N*M<<1];
	inline void addedge(int u,int v,int f){
		e[++_hd]=(edge){v,f,hd[u]};
		hd[u]=_hd;
		e[++_hd]=(edge){u,0,hd[v]};
		hd[v]=_hd;
	}
	
	inline void init(int n_,int s_,int t_){
		for(int i=1;i<=n;i++)
			hd[i]=0;
		_hd=1;
		n=n_,s=s_,t=t_;
	}
	
	std::queue<int> q;
	int cur[N],dis[N];
	inline bool bfs(){
		for(int i=1;i<=n;i++)
			cur[i]=hd[i];
		for(int i=1;i<=n;i++)
			dis[i]=inf;
		dis[s]=0;
		q.push(s);
		while(q.size()){
			int u=q.front();
			q.pop();
			for(int i=hd[u];i;i=e[i].nxt){
				int v=e[i].v,f=e[i].f;
				if(f&&dis[v]>dis[u]+1){
					dis[v]=dis[u]+1;
					q.push(v);
				}
			}
		}
		return dis[t]<inf;
	}
	inline int dfs(int u,int lmt){
		if(u==t||!lmt)
			return lmt;
		int res=0;
		for(int i=cur[u];i;i=e[i].nxt){
			cur[u]=i;
			int v=e[i].v,f=e[i].f;
			if(dis[v]!=dis[u]+1)
				continue;
			f=dfs(v,std::min(lmt,f));
			e[i].f-=f,e[i^1].f+=f;
			lmt-=f,res+=f;
			if(!lmt)
				break;
		}
		return res;
	}
	inline void sol(){
		while(bfs())
			dfs(s,inf);
	}
}

int n,m,y[N],a[M],b[M];
ull c[N];

std::vector<int> E[N];

int f[N],p[N],id[N],q[N];
inline void sol(int L,int R,int l,int r){
	if(l>r)
		return;
	if(L==R){
		for(int i=l;i<=r;i++)
			f[p[i]]=L;
		return;
	}
	int mid=(L+R)>>1;
	MF::init(r-l+3,r-l+2,r-l+3);
	for(int i=l;i<=r;i++)
		id[p[i]]=i-l+1;
	for(int i=l;i<=r;i++){
		int u=p[i],w=2*(y[u]-mid)-1;
		if(w>0)
			MF::addedge(MF::s,id[u],w);
		else
			MF::addedge(id[u],MF::t,-w);
		for(auto v:E[u])
			if(id[v])
				MF::addedge(id[u],id[v],inf);
	}
	MF::sol();
	int pm=l-1;
	for(int i=l;i<=r;i++)
		if(MF::dis[id[p[i]]]==inf)
			q[++pm]=p[i];
	int tmp=pm;
	for(int i=l;i<=r;i++)
		if(MF::dis[id[p[i]]]<inf)
			q[++tmp]=p[i];
	for(int i=l;i<=r;i++)
		p[i]=q[i];
	for(int i=l;i<=r;i++)
		id[p[i]]=0;
	sol(L,mid,l,pm);
	sol(mid+1,R,pm+1,r);
}

ull ans;

int main(){
	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",&y[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::cl();
		for(int j=1;j<=m;j++)
			if(j!=i)
				XB::ins(c[a[j]]);
		for(int j=1;j<=n;j++)
			if(j!=a[i]&&XB::chk(c[j]))
				E[a[i]].push_back(j);
	}
	for(int i=1;i<=m;i++){
		XB::cl();
		for(int j=1;j<=m;j++)
			if(j!=i)
				XB::ins(c[b[j]]);
		for(int j=1;j<=n;j++)
			if(j!=b[i]&&XB::chk(c[j]))
				E[j].push_back(b[i]);
	}
	for(int i=1;i<=n;i++)
		p[i]=i;
	sol(0,(int)1e6,1,n);
	for(int i=1;i<=n;i++)
		ans+=1ull*(f[i]-y[i])*(f[i]-y[i]);
	printf("%llu\n",ans);
}