拿小号打了这场,然而做到这里时少看了条件,最后 10min 才发现,没有 AK,身败名裂……
赛后看就是 sb 题……
(好像这题也不值 2500 吧?)
首先注意到一条很重要的条件:对于每棵树,都存在一种 DFS 序使得叶子被访问到的顺序就是与它相连的用电器的编号。
这说明,对于每棵树的任意子树,里面所有叶子对应的用电器的编号是连续的。
既然连续就能方便 DP 了。
正着做不好设计状态。反过来,求最少能保留多少条边。
那么就是每个用电器对应的叶子到根上的边都要选。没有限制的全部能删掉。
然后由于用电器对应的叶子的 DFS 序递增,所以对于一个用电器集合 \(\{S\}(|S|\ge 2)\),如果这里面每个用电器都和同一个根相连,那么 \(S\) 的花费 \(cost_S\) 是 \(cost_{S-x}+dep_x-dep_{lca(x,y)}\),其中 \(x\) 是 \(S\) 中编号最大的用电器,\(y\) 是 \(S\) 中编号第二大的用电器。
上 DP。
先记 \(f_{i,j,0}(i<j)\) 表示第 \(i\) 个用电器和第 \(j\) 个用电器如果都选第 \(0\) 棵树时,\(dep_{x_i}-dep_{lca(x_i,x_j)}\) 的值。\(f_{i,j,1}\) 同理。
再记 \(fpre_{i,j,0}(i\le j)\) 表示第 \(i\) 个用电器到第 \(j\) 个用电器都选在第 \(0\) 棵树时的总花费。大概是个类似前缀和的东西。\(fpre_{i,j,1}\) 同理。
再记 \(dp_{i,j,0}(i>j)\) 表示只考虑前 \(i\) 个用电器,第 \(j\) 到第 \(i\) 个用电器都选在第 \(0\) 棵树,且第 \(j-1\) 个用电器选在第 \(1\) 棵树的最小花费。\(dp_{i,j,1}\) 同理。
转移,枚举第 \(k\) 到第 \(j-1\) 个用电器选在第 \(1\) 棵树(且 \(k-1\) 选在第 \(0\) 棵树)。\(dp_{i,j,0}=fpre_{j,i,0}+\min(dp_{j-1,k,1}+f_{k-1,j,0}-(dep_{x_j}[k\ne 1]))\)。
解释一下。
\(fpre_{j,i,0}\) 就是 \(j\) 到 \(i\) 的花费。
\(dp_{j-1,k,1}\) 就是 \(k\) 到 \(j-1\) 的最小花费。
\(f_{k-1,j,0}\) 是因为:考虑从小到大加入 \(j\) 到 \(i\),按上文说的最大编号和次大编号计算贡献。所以加入 \(j\) 时,就会比原来的花费多 \(dep_{x_j}-dep_{lca(x_j,x_{k-1})}\),也就是 \(f_{k-1,j,0}\)。
当 \(k\ne 1\) 时,由于 \(fpre\) 中算的贡献中 \(j\) 是要自力更生的,但是实际上此时 \(k-1\) 可以给 \(j\) 一些已经用过的边(这个费用就是上面的 \(f_{k-1,j,0}\))。所以要把 \(dep_{x_j}\) 减掉。
这是个 \(O(n^3)\) 做法。
优化也很显然。设 \(mn_{i,0}=\min(dp_{i-1,j,1}+f_{j-1,i,0}-(dep_{x_i}[j\ne 1]))\)。\(mn_{i,1}\) 同理。
那么有 \(dp_{i,j,0}=fpre_{j,i,0}+mn_{j,0}\)。
时间复杂度 \(O(n^2)\)。
传说有 \(O(n)\) 做法,但我不会……
代码中略微有一点不一样,稍微注意。
代码
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int,int> PII; const int maxn=2222; #define MP make_pair #define PB push_back #define lson o<<1,l,mid #define rson o<<1|1,mid+1,r #define FOR(i,a,b) for(int i=(a);i<=(b);i++) #define ROF(i,a,b) for(int i=(a);i>=(b);i--) #define MEM(x,v) memset(x,v,sizeof(x)) inline ll read(){ char ch=getchar();ll x=0,f=0; while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar(); while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar(); return f?-x:x; } int n,a,b,x[maxn],y[maxn],el,head[maxn],to[maxn],nxt[maxn],f[maxn][maxn][2],fpre[maxn][maxn][2],dp[maxn][maxn][2],mn[maxn][2]; int fa[maxn],sz[maxn],son[maxn],dep[maxn],top[maxn]; inline void add(int u,int v){ to[++el]=v;nxt[el]=head[u];head[u]=el; } void dfs1(int u,int f){ dep[u]=dep[fa[u]=f]+1; sz[u]=1; for(int i=head[u];i;i=nxt[i]){ int v=to[i]; dfs1(v,u); sz[u]+=sz[v]; if(sz[v]>sz[son[u]]) son[u]=v; } } void dfs2(int u,int topf){ top[u]=topf; if(son[u]) dfs2(son[u],topf); for(int i=head[u];i;i=nxt[i]){ int v=to[i]; if(v==son[u]) continue; dfs2(v,v); } } int lca(int u,int v){ while(top[u]!=top[v]){ if(dep[top[u]]<dep[top[v]]) swap(u,v); u=fa[top[u]]; } return dep[u]<dep[v]?u:v; } int main(){ n=read(); a=read(); FOR(i,2,a) add(read(),i); dep[0]=-1; dfs1(1,0);dfs2(1,1); FOR(i,1,n) x[i]=read(); ROF(i,n,1){ int s=0; FOR(j,i,n){ if(i==j) s=f[i][j][0]=dep[x[j]]; else s+=f[i][j][0]=dep[x[j]]-dep[lca(x[j],x[i])]; fpre[i][j][0]=fpre[i][j-1][0]+(i==j?f[j][j][0]:f[j-1][j][0]); } } el=0;MEM(head,0);MEM(to,0);MEM(nxt,0);MEM(fa,0);MEM(sz,0);MEM(son,0);MEM(dep,0);MEM(top,0); b=read(); FOR(i,2,b) add(read(),i); dep[0]=-1; dfs1(1,0);dfs2(1,1); FOR(i,1,n) y[i]=read(); ROF(i,n,1){ int s=0; FOR(j,i,n){ if(i==j) s=f[i][j][1]=dep[y[j]]; else s+=f[i][j][1]=dep[y[j]]-dep[lca(y[j],y[i])]; fpre[i][j][1]=fpre[i][j-1][1]+(i==j?f[j][j][1]:f[j-1][j][1]); } } MEM(dp,0x3f);MEM(mn,0x3f); dp[0][0][0]=dp[0][0][1]=mn[1][0]=mn[1][1]=0; FOR(i,1,n) FOR(j,1,i){ dp[i][j][0]=fpre[j][i][0]+mn[j][1]; dp[i][j][1]=fpre[j][i][1]+mn[j][0]; if(i!=n){ mn[i+1][0]=min(mn[i+1][0],dp[i][j][0]+(j==1?0:f[j-1][i+1][1]-f[i+1][i+1][1])); mn[i+1][1]=min(mn[i+1][1],dp[i][j][1]+(j==1?0:f[j-1][i+1][0]-f[i+1][i+1][0])); } } int ans=1e9; FOR(j,1,n) ans=min(ans,min(dp[n][j][0],dp[n][j][1])); printf("%d\n",a+b-2-ans); }