hdu 6065 RXD, tree and sequence
题
OwO http://acm.hdu.edu.cn/showproblem.php?pid=6065
(2017 Multi-University Training Contest - Team 3 - 1010)
解
首先,一个连续段的LCA就是每相邻两个点的LCA的最小值
证明:
假设有一段区间s1~sn,称这段区间为S,他们的LCA是U,那么U必然存在多个后继,这里为了方便假设U存在2个后继,若后继不止2个,可类似得到结论
那么这两个后继就有对应的2个子树A,B,由于S的LCA是U,所以A,B中均有S的元素。那么设S中在子树A中的元素为a1~ap,在子树B中的元素为b1~bq。
不妨设s1存在于A中。对于si,如果si+1和si不同在A或不同在B中,那么对于si和si+1,他们的LCA就是u
那么,如果不存在相邻两点他们LCA为u的话,对于任意si,si和si+1他们同在A或同在B,由于s1在A,所以s1~sn全在A中,那么区间S中就没有B的元素,则S的LCA就不是U,矛盾。
那么可以先预处理出每两个相邻点的LCA
然后声明一个dp数组dp[i][j]的意义为,从1~i分成j段的最小答案。
把P分成k个连续段P1,P2…Pn,则设这些段中LCA深度最小的相邻两点对为pair1,pair2,pair3…pairn,那么我们可以把它们当做由这些pair领头的序列(开头可以出现一段不计入答案的序列)(如果这些段只有一个点那么就不变)
这样的dp[i][j]就可以由以下3种方式推导过来
1.dp[i][j]=dp[i-1][j] 相当于把第j段扩展下去,由于最小两对是开头,所以不用更新其值
2.dp[i][j]=dp[i-2][j-1]+depth[LCA(P[i-1],P[i])] 就是从分成j-1段那里递推来,然后了个开头(开头为2两个相邻点,这两个相邻点当做第j段LCA深度最小的相邻点)
3.dp[i][j]=dp[i-1][j-1]+depth[P[i]] 也是从分成j-1段那里递推过来,只不过开头是一个点,
这样dp[n][k]就是答案
(思路来自某位大佬 orz)
#include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #include <vector> #include <queue> #include <set> #include <map> #include <string> #include <math.h> #include <stdlib.h> #include <time.h> using namespace std; const int M=3e5+55; const int MAXN = 340044; const int MAXQ = 500010; int F[MAXN];//需要初始化为-1 int find(int x) { if(F[x] == -1)return x; return F[x] = find(F[x]); } void bing(int u,int v) { int t1 = find(u); int t2 = find(v); if(t1 != t2) F[t1] = t2; } bool vis[MAXN];//访问标记 int ancestor[MAXN];//祖先 struct Edge { int to,next; }edge[MAXN*2]; int head[MAXN],tot; void addedge(int u,int v) { edge[tot].to = v; edge[tot].next = head[u]; head[u] = tot++; } struct Query { int q,next; int index;//查询编号 }query[MAXQ*2]; int answer[MAXQ];//存储最后的查询结果,下标0~Q-1 int h[MAXQ]; int tt; int Q; void add_query(int u,int v,int index) { query[tt].q = v; query[tt].next = h[u]; query[tt].index = index; h[u] = tt++; query[tt].q = u; query[tt].next = h[v]; query[tt].index = index; h[v] = tt++; } void init() { tot = 0; memset(head,-1,sizeof(head)); tt = 0; memset(h,-1,sizeof(h)); memset(vis,false,sizeof(vis)); memset(F,-1,sizeof(F)); memset(ancestor,0,sizeof(ancestor)); } void LCA(int u) { ancestor[u] = u; vis[u] = true; for(int i = head[u];i != -1;i = edge[i].next) { int v = edge[i].to; if(vis[v])continue; LCA(v); bing(u,v); ancestor[find(u)] = u; } for(int i = h[u];i != -1;i = query[i].next) { int v = query[i].q; if(vis[v]) { answer[query[i].index] = ancestor[find(v)]; } } } int dep[M]; int n,k; int s[M]; int ans; vector<vector<int> > dp; void getdep(int rt,int pa,int depth) { int i,j,v; dep[rt]=depth; for(i=head[rt];i!=-1;i=edge[i].next) { v=edge[i].to; if(v==pa) continue; getdep(v,rt,depth+1); } } void solve() { int i,j,tmp; getdep(1,-1,1); for(i=0;i<=n;i++) dp[i][0]=0; for(i=1;i<=n;i++) for(j=1;j<=min(i,k);j++) { tmp=1e9+7; if(i-1>=1) tmp=min(tmp,dp[i-2][j-1]+dep[answer[i-1-1]]); // cout<<i<<' '<<j<<' '<<tmp<<endl; tmp=min(tmp,dp[i-1][j-1]+dep[s[i]]); if(i-1>=j) tmp=min(tmp,dp[i-1][j]); // cout<<i<<' '<<j<<' '<<tmp<<endl; dp[i][j]=tmp; // cout<<i<<' '<<j<<' '<<tmp<<endl; } ans=dp[n][k]; cout<<ans<<endl; } int main() { int i,j; int u,v; while(scanf("%d%d",&n,&k)!=EOF) { dp.assign(n+2,vector<int>(k+2,1e9+7)); init(); for(i=1;i<=n;i++) scanf("%d",&s[i]); for(i=1;i<n;i++) { scanf("%d%d",&u,&v); addedge(u,v); addedge(v,u); } Q=n-1; for(i=0;i<Q;i++) add_query(s[1+i],s[1+i+1],i); LCA(1); solve(); } return 0; }