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;
}

  

  

hdu 6065 RXD, tree and sequence的更多相关文章

  1. HDU 6065 RXD, tree and sequence (LCA DP)

    RXD, tree and sequence Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 524288/524288 K (Java ...

  2. HDU 6065 RXD, tree and sequence (LCA+DP)

    题意:给定上一棵树和一个排列,然后问你把这个排列分成m个连续的部分,每个部分的大小的是两两相邻的LCA的最小深度,问你最小是多少. 析:首先这个肯定是DP,然后每个部分其实就是里面最小的那个LCA的深 ...

  3. RXD, tree and sequence IN HDU6065

    解这道题绕了好多弯路...先是把"depth of the least common ancestor"这句话忽视掉,以为是最深点与最浅点的深度差:看到某人题解(的开头)之后发现自 ...

  4. 【Tarjan】【LCA】【动态规划】【推导】hdu6065 RXD, tree and sequence

    划分出来的每个区间的答案,其实就是连续两个的lca的最小值. 即5 2 3 4 这个区间的答案是min(dep(lca(5,2)),dep(lca(2,3),dep(lca(3,4)))). 于是dp ...

  5. HDU 5513 Efficient Tree

    HDU 5513 Efficient Tree 题意 给一个\(N \times M(N \le 800, M \le 7)\)矩形. 已知每个点\((i-1, j)\)和\((i,j-1)\)连边的 ...

  6. ※数据结构※→☆非线性结构(tree)☆============二叉树 顺序存储结构(tree binary sequence)(十九)

    二叉树 在计算机科学中,二叉树是每个结点最多有两个子树的有序树.通常子树的根被称作“左子树”(left subtree)和“右子树”(right subtree).二叉树常被用作二叉查找树和二叉堆或是 ...

  7. HDU 4925 Apple Tree(推理)

    HDU 4925 Apple Tree 题目链接 题意:给一个m*n矩阵种树,每一个位置能够选择种树或者施肥,假设种上去的位置就不能施肥,假设施肥则能让周围果树产量乘2.问最大收益 思路:推理得到肯定 ...

  8. 2017 多校3 hdu 6061 RXD and functions

    2017 多校3 hdu 6061 RXD and functions(FFT) 题意: 给一个函数\(f(x)=\sum_{i=0}^{n}c_i \cdot x^{i}\) 求\(g(x) = f ...

  9. HDU 4871 Shortest-path tree 最短路 + 树分治

    题意: 输入一个带权的无向连通图 定义以顶点\(u\)为根的最短路生成树为: 树上任何点\(v\)到\(u\)的距离都是原图最短的,如果有多条最短路,取字典序最小的那条. 然后询问生成树上恰好包含\( ...

随机推荐

  1. 取整math函数

    floor(a); ceil(a);  

  2. 斜率优化DP(转载)

    转载自:https://www.cnblogs.com/ka200812/archive/2012/08/03/2621345.html 我们知道,有些DP方程可以转化成DP[i]=f[j]+x[i] ...

  3. (0)c++入门——认识指针与数组——指针即是内存中地址。

    初识指针 首先需要了解一个概念,计算机的内存(或者说是寄存器)都是有地址的. <c++ primer plus>一书P37中提到这样一个概念:为把信息存储在计算机中,程序必须记录3个基本属 ...

  4. winform串口控件serialPort1的使用

    serialPort1 控件使用的关键点主要有三: 1.配置串口号2.配置数据接收事件3.打开串口 关键代码如下: private void Form1_Load(object sender, Eve ...

  5. oracle修改TNSLSNR的端口

    oracle 服务一启动 TNSLSNR.exe 会占用8080端口,这时,如果我们其他程序需要使用8080端口就会比较麻烦,所以需要改一下端口: 用dba账户登录 CMD>sqlplus sy ...

  6. 【spring Boot】spring boot1.5以上版本@ConfigurationProperties取消location注解后的替代方案

    前言 =========================================== 初步接触Spring Boot ===================================== ...

  7. JS基础_相等运算符

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  8. mark ubuntu 16.04 64bit + cpu only install mtcnn

    大神代码链接 称之为MTCNN人脸检测算法,同时有大神已经GitHub上开源了其基于caffe的C++ API 的源代码,https://github.com/DaFuCoding/MTCNN_Caf ...

  9. js之数据类型(对象类型——构造器对象——数组2)

    一.数组空位与undefined 数组空位:数组的某一个位置没有任何值 产生空位的原因:数组中两个逗号之间不放任何值:用new Array()的方法,参数里是个数字:通过一个不存在的下标去增加数组:增 ...

  10. cookie以一个或多个空格开头

    作为一个整体,如果cookie以一个或多个空格开头,请用空格替换所有空格:如果cookie以一个或多个空格结尾,请用空格替换所有空格. 象征意义: \ S:空间,空间 +一个或多个 ^开始,^s,以空 ...