题解:QOJ5363 [北大集训2018] ZYB 的游览计划 / SS221028D journey

posted on 2022-11-05 18:42:17 | under 题解 | source

前置知识:CF868F

problem

一棵 \(n\) 个点的树。定义序列 \(v=\{v_1,v_2,\cdots,v_k\}\) 的权值为

\[\min_{p\text{ is a permutation}}\left\{ dist(1,v_{p_1})+ \sum_{1<i\leq k} dist(v_{p_{i-1}},v_{p_i})+ dist(v_{p_k},1) \right\}. \]

试将长为 \(n\) 的排列 \(a\) 划分为 \(k\) 段(每段非空),使每一段的权值和最大。\(nk\leq 10^5\)

solution 0

考虑如何计算一段的权值?

贪心,按照 dfn 序将 \(v\) 排序应该是最优的。于是我们可以 \(O(n\log n)\) 求出 \(V(l,r)\)

所以有 \(f_{i,k}=\max_{j<i}\{f_{j,k-1}+V(j+1,i)\}\)。硬冲即可拿到 10 分。

solution 1

考虑 \(V(l,r)\) 有什么性质?

1. 支持插入删除

动态维护一个 std::set,每次插入更新一下相邻两个点的距离,删除也是,那么我们在知道 \(V(l,r)\) 的情况下可以快速(\(O(\log n)\))求出 \(V(l,r\pm1),V(l\pm1,r)\)

2. 满足四边形不等式

考虑四边形不等式:相交小于包含。

相交:

	|___________|
|__________|

包含:

	|______|
|_______________|

由于这题求 \(\max\),我们反过来变成相交大于包含。感性理解一下,虚树中的点越多真正建出来的点越少,所以成立。

3. 满足决策单调性

显然。

solution 2

那么我们对着决策单调性做文章。

考虑分治:假如我们已经有划分成 \(d-1\) 段的答案,欲求所有的 \(f_{i,d}\)。考虑欲求 \(f_{[L,R],d}\),现在有效的决策集合是 \([l,r]\)

我们求出 \(f_{mid,d}\) 的值:用 \([l,r]\) 的决策更新。假如找到一个最优决策在 \(p\)

那么我们断定:\([L,mid)\) 的点的决策为 \([l,p]\)\((mid,R]\) 的点的决策为 \([p,r]\)

分析复杂度:一共 \(O(\log n)\) 层,每层平摊 \(O(n)\)

我们的 \(V(l,r)\) 的复杂度如何分析?

考虑左右指针是独立的;考虑对于一个指针,在分治树上的每一个点恰好踩过一次,所以移动量为 \(O(n\log n)\)

所以总复杂度为 \(O(n\log^2 n)\)

code

#include <set>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
template<int N,int M,class T=int> struct graph{
	int head[N+10],nxt[M*2+10],cnt;
	struct edge{
		int u,v; T w;
		edge(int u=0,int v=0,T w=0):u(u),v(v),w(w){}
	} e[M*2+10];
	graph(){memset(head,cnt=0,sizeof head);}
	edge&operator[](int i){return e[i];}
	void add(int u,int v,T w=0){e[++cnt]=edge(u,v,w),nxt[cnt]=head[u],head[u]=cnt;}
	void link(int u,int v,T w=0){add(u,v,w),add(v,u,w);}
};
template<int N,int M,class T=int> struct treecut: public graph<N,M,T>{
	graph<N,M,T>&g=*this;
	int fa[N+10],dep[N+10],siz[N+10],son[N+10],
		dfn[N+10],rnk[N+10],top[N+10],cnt;
	treecut(){memset(son,siz[0]=cnt=0,sizeof son);}
	void dfs(int u,int f=0){
		dep[u]=dep[fa[u]=f]+1,siz[u]=1;
		for(int i=g.head[u];i;i=g.nxt[i]){
			int v=g[i].v; if(v==f) continue;
			dfs(v,u),siz[u]+=siz[v];
			if(siz[v]>siz[son[u]]) son[u]=v;
		}
	}
	void cut(int u,int topf){
		top[rnk[dfn[u]=++cnt]=u]=topf;
		if(son[u]) cut(son[u],topf);
		for(int i=g.head[u];i;i=g.nxt[i]){
			int v=g[i].v; if(v==fa[u]||v==son[u]) continue;
			cut(v,v);
		}
	}
	int lca(int u,int v){
		for(;top[u]!=top[v];u=fa[top[u]]){
			if(dep[top[u]]<dep[top[v]]) swap(u,v);
		}
		if(dep[u]<dep[v]) swap(u,v);
		return v;
	}
	int dist(int u,int v){return dep[u]+dep[v]-2*dep[lca(u,v)];}
};
int n,m,p[200010];
treecut<200010,200010> g;
struct ds{
	set<int> s; LL sum,l,r;
	ds(){clear();}
	void clear(){sum=0,l=1,r=0,s.clear(),s.insert(1),s.insert(n+1);}
	void add(int u){
		if(u==1) return ;
		set<int>::iterator it=s.lower_bound(g.dfn[u]);
		int pre=g.rnk[*prev(it)],nxt=g.rnk[*it];
		sum+=-g.dist(pre,nxt)+g.dist(u,pre)+g.dist(u,nxt);
		s.insert(g.dfn[u]);
	}
	void del(int u){
		if(u==1) return ;
		set<int>::iterator it=s.lower_bound(g.dfn[u]);
		int pre=g.rnk[*prev(it)],nxt=g.rnk[*next(it)];
		sum+=-g.dist(u,pre)-g.dist(u,nxt)+g.dist(pre,nxt);
		s.erase(it);
	}
	LL operator()(int L,int R){
		while(r<R) add(p[++r]);
		while(L<l) add(p[--l]);
		while(R<r) del(p[r--]);
		while(l<L) del(p[l++]);
		return sum;
	}
};
LL f[2][200010];
ds calc;
void solve(int L,int R,int l,int r,int d){
	if(L>R) return ;
	int mid=(L+R)>>1,p;
	LL&res=f[d][mid]=-1e18;
	for(int i=max(1,l);i<=min(r,mid-1);i++){
		LL v=f[d^1][i]+calc(i+1,mid);
		if(v>res) res=v,p=i;
	}
	solve(L,mid-1,l,p,d),solve(mid+1,R,p,r,d);
}
int main(){
freopen("journey.in","r",stdin),freopen("journey.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&p[i]);
	for(int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),g.link(u,v);
	g.dfs(1),g.cut(1,1);
	g.dep[n+1]=g.dep[1],g.rnk[n+1]=1;
	calc.clear();
	for(int i=1;i<=n;i++) f[1][i]=calc(1,i);
	for(int i=2;i<=m;i++) solve(1,n,1,n,i&1);
	printf("%lld\n",f[m&1][n]);
	return 0;
}
posted @ 2022-11-17 10:41  caijianhong  阅读(8)  评论(0)    收藏  举报