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

  

  

原文地址:https://www.cnblogs.com/FxxL/p/7274279.html