hdu 4916 Count on the path 树dp

2014多校05场第6题


挺棒的树dp

题目每次询问给两个节点,问不在这两个节点的路径上的最小节点编号是多少

首先,如果两个节点的路径不经过点1,显然最小编号是1

如果经过1的话,那么以1为根,这两个点到1的路径肯定来自不同的子树,假设这两个点分别是a和b

然后最小的节点就是{a所在子树中去掉a到1的路径最小的点,b所在子树中去掉b到1的路径最小的点,其他子树中最小的点}中最小的点

每个点属于哪个子树很好处理,每个子树最小的点是谁也很好处理

然后就是处理“点x所在子树中除去x到根的路径外的最小的节点”

这个东西是{点x的所有子树中的最小节点,从根走过来一路上的分支子树中的最小节点}中的最小节点

而{从根走到x一路上的分支子树中的最小节点}是{从根走到x的父亲一路上的分支子树中的最小节点}和{x的父亲除了x所在子树的所有子树的最小节点}中的最小

然后问题就解决了


#include <stdio.h>
#include <string>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <limits.h>
#include <math.h>
using namespace std;

#pragma comment(linker, "/STACK:102400000,102400000")

typedef pair<int,int> PII;
typedef long long LL;
#define CLR(x,y) memset(x,y,sizeof(x));
#define PB push_back
#define MP make_pair
#define INF 0x3f3f3f3f
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1

const int N = 1000000+20;

inline int Min(int a,int b){
    return a < b ? a : b;
}
int get() {
    char c;
    while(c=getchar(),(c<'0'||c>'9')&&(c!='-'));
    bool flag=(c=='-');
    if(flag)
        c=getchar();
    int x=0;
    while(c>='0'&&c<='9') {
        x=x*10+c-'0';
        c=getchar();
    }
    return flag?-x:x;
}

void output(long long x) {
    if(x<0) {
        putchar('-');
        x=-x;
    }
    int len=0,data[20];
    while(x) {
        data[len++]=x%10;
        x/=10;
    }
    if(!len)
        data[len++]=0;
    while(len--)
        putchar(data[len]+'0');
    putchar('\n');
}

struct node
{
    int v,w;
    int nxt;
}edge[N << 1];

int head[N],idx;
void init()
{
    CLR(head, -1);
    idx = 0;
}

void add_edge(int u,int v,int w)
{
    edge[idx].v = v;
    edge[idx].w = w;
    edge[idx].nxt = head[u];
    head[u] = idx ++;
    
    edge[idx].v = u;
    edge[idx].w = w;
    edge[idx].nxt = head[v];
    head[v] = idx++;
}

int n,q;
int tot;
int bel[N];
int fa[N];
int mm[N];
int xm[N][2];
int sm[N];

multiset<int> mii;
multiset<int>::iterator ite;

void update(int s,int val)
{
    if(val < xm[s][0]){
        xm[s][1] = xm[s][0];
        xm[s][0] = val;
    }else if(val < xm[s][1]){
        xm[s][1] = val;
    }
}

int search(int s,int mark)
{
    bel[s] = mark;
    for(int i = head[s]; ~i ; i = edge[i].nxt){
        int v = edge[i].v;
        if(v != fa[s]){
            fa[v] = s;
            update(s, Min(search(v,mark),v));
        }
    }
    return xm[s][0];
}

void dfs(int s)
{
     for(int i = head[s]; ~i ; i = edge[i].nxt){
        int v = edge[i].v;
        if(v != fa[s]){
            sm[v] = Min(sm[s],(Min(xm[v][0],v)==xm[s][0]?xm[s][1]:xm[s][0]));
            mm[v] = Min(sm[v],xm[v][0]);
            dfs(v);
        }
    }
}

void solve()
{
    CLR(fa, -1);
    CLR(xm, 0x3f);
    CLR(sm, 0x3f);
    CLR(mm, 0x3f);
    bel[1] = n+1;
    tot = 0;
    int cnt = 0;
    for(int i = head[1]; ~i ; i = edge[i].nxt){
        int v = edge[i].v;
        fa[v] = 1;
        search(v,v);
        dfs(v);
        mm[v] = Min(sm[v],xm[v][0]);
        cnt ++;
    }
    mii.clear();
    for(int i = head[1];  ~i ; i = edge[i].nxt){
        int v = edge[i].v;
        int val = Min(v,mm[v]);
        mii.insert(val);
        if(mii.size() > 3){
            ite = mii.end();
            ite--;
            mii.erase(ite);
        }
    }
}

int work(int u,int v)
{
    if(bel[u] == bel[v])return 1;
    int a = Min(bel[u],mm[bel[u]]);
    int b = Min(bel[v],mm[bel[v]]);
    int m = 0;
    for(ite = mii.begin();ite != mii.end() ;ite++){
        int val = *ite;
        if(val == a){
            a = -1;
        }else{
            if(val == b){
                b = -1;
            }else{
                m = val;
                break;
            }
        }
    }
    return Min(m,Min(mm[u],mm[v]));
}

int main()
{
   // freopen("/Users/minli/Documents/Developer/Testebd/cpp/cpp/input.txt", "r", stdin);
   // freopen("/Users/minli/Documents/Developer/Testebd/cpp/cpp/output.txt", "w", stdout);
    while(~scanf("%d%d",&n,&q)){
        init();
        for(int i = 0 ; i < n-1 ; i ++){
            int a,b;
            a = get();
            b = get();
            add_edge(a, b, 1);
        }
        solve();
        int ans = -1;
        while(q--){
            int a,b;
            a = get();
            b = get();
            if(ans != -1){
                a = a ^ ans;b = b ^ ans;
            }
            ans = work(a,b);
            output(ans);
        }
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值