hdu 4916 Count on the path(树形dp)

题意:给出一颗树,q个询问,每个询问要求回答不在给出的路径上的点的最小值。

思路:以1为根节点,对于查询,如果这条路径不经过1,那么显然答案就是1。否则,将整棵树划分为几个以1节点的儿子为根节点的子树,那么答案就是两个端点所属的子树中去掉相应路径上的点的最小值,在与其他子树节点的最小值取一个最小值。对于一个节点u,设其相应根节点为root,那么dp[u]的值就是以root为根的子树,去掉root到u的路径上的点以后的最小值,这个可以先求个子树最小值,然后再dfs算一遍就能得到了。对于给定的两个端点,要求它们是在以1的哪个儿子为根的子树,这个用并查集搞一下就好了。


代码:


#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<set>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=1000000+10;
struct Edge
{
    int v,next;
    Edge(int v=0,int next=0):v(v),next(next){}
}edges[maxn<<1];
int head[maxn],nEdge;
pair<int,int>minv[maxn][4],rootval[4];
int dp[maxn],pa[maxn];
int Find(int x)
{
    return x==pa[x]?x:pa[x]=Find(pa[x]);
}
void AddEdges(int u,int v)
{
    edges[++nEdge] = Edge(v, head[u]);
    head[u] = nEdge;
    edges[++nEdge] = Edge(u, head[v]);
    head[v] = nEdge;
}
void dfs1(int u,int fa)
{
    minv[u][1] = minv[u][2] = minv[u][3] = make_pair(inf,inf);
    minv[u][0] = make_pair(u,u);
    for(int k = head[u]; k != -1; k=edges[k].next)
    {
        int v = edges[k].v;
        if(v == fa) continue;
        dfs1(v,u);
        pa[v]=u;
        minv[u][3] = make_pair(minv[v][0].first,v);
        sort(minv[u],minv[u] + 4);
    }
}
void dfs2(int u,int fa,int mval)
{
    int tmp=mval;
    if(minv[u][0].second == u)
        tmp = min(tmp,minv[u][1].first);
    else tmp = min(tmp,minv[u][0].first);
    dp[u] = tmp;
    for(int k = head[u];k != -1;k=edges[k].next)
    {
        int v = edges[k].v;
        if(v == fa) continue ;
        tmp = mval;
        if(minv[u][0].second != u && minv[u][0].second != v)
            tmp=min(tmp,minv[u][0].first);
        else if(minv[u][1].second != u && minv[u][1].second != v)
            tmp=min(tmp,minv[u][1].first);
        else tmp=min(tmp,minv[u][2].first);
        dfs2(v,u,tmp);
    }
}
int main()
{
//    freopen("in.txt","r",stdin);
//    freopen("out.txt","w",stdout);
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        memset(head ,0xff,sizeof(head));
        nEdge = -1;
        int u,v;
        for(int i = 1;i < n;++i)
        {
            scanf("%d%d",&u,&v);
            AddEdges(u,v);
        }
        dfs1(1,-1);
        dp[1]=inf;
        rootval[0] =rootval[1] =rootval[2] =rootval[3] = make_pair(inf,inf);
        for(int k = head[1];k != -1;k=edges[k].next)
        {
            v = edges[k].v;
            dfs2(v,1,inf);
            rootval[3] = make_pair(minv[v][0].first,v);
            sort(rootval , rootval + 4);
            pa[v] = v;
        }
        int last = 0;
        while(m--)
        {
            scanf("%d%d",&u,&v);
            u ^= last;
            v ^= last;
            if(Find(u) == Find(v)) last = 1;
            else
            {
                last = min(dp[u] ,dp[v]);
                u = Find(u);
                v = Find(v);
                if(rootval[0].second != u && rootval[0].second != v)
                    last = min(last,rootval[0].first);
                else if(rootval[1].second != u && rootval[1].second != v)
                    last = min(last,rootval[1].first);
                else last = min(last,rootval[2].first);
            }
            printf("%d\n",last);
        }
    }
    return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值