题解:QOJ5363 [北大集训2018] ZYB 的游览计划 / SS221028D journey
posted on 2022-11-05 18:42:17 | under 题解 | source
problem
一棵 \(n\) 个点的树。定义序列 \(v=\{v_1,v_2,\cdots,v_k\}\) 的权值为
试将长为 \(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;
}
本文来自博客园,作者:caijianhong,转载请注明原文链接:https://www.cnblogs.com/caijianhong/p/solution-SS221028D.html
浙公网安备 33010602011771号