HDU - 4916 Count on the path

题意:

给定一棵有 n n n 个结点的树,再有 q q q 次询问,每次询问 u u u v v v 路径上没有出现的最小编号(结点)。 ( n , q ≤ 1 0 6 ) (n,q \leq 10^6) (n,q106)

链接:

https://vjudge.net/problem/HDU-4916

解题思路:

如果数量级是 10^5 我将用主席树.。以 1 1 1 号结点为根,若 u , v u,v u,v l c a lca lca 不为 1 1 1,则答案为 1 1 1,否则答案 u , v u,v u,v 路径可以分解成两条从根开始的路径,记分别在根往下的 b e l [ u ] bel[u] bel[u] b e l [ v ] bel[v] bel[v] 子树内,树形 d p dp dp 预处理出 f m n [ u ] fmn[u] fmn[u],表示 u u u b e l [ u ] bel[u] bel[u] 子树内除了 u u u b e l [ u ] bel[u] bel[u] 路径上结点外的最小编号(结点),则对于单次询问,答案至少为 m i n { f m n [ u ] , f m n [ v ] } min\{fmn[u], fmn[v]\} min{fmn[u],fmn[v]},此外还需考虑根结点除了 b e l [ u ] , b e l [ v ] bel[u],bel[v] bel[u],bel[v] 子树外的其他子树里的最小值,对应维护即可。
 
数据量大,请用快读。

参考代码:
#include<bits/stdc++.h>
 
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define sz(a) ((int)a.size())
#define pb push_back
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 1e6 + 5;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
 
struct Edge{
    int v, nxt;
} edge[maxn << 1];
int head[maxn], cnt;
int fmn[maxn], mn[maxn][3], pmn[maxn][3], bel[maxn];
int n, q;

void add(int u, int v){

    edge[++cnt] = {v, head[u]}, head[u] = cnt;
    edge[++cnt] = {u, head[v]}, head[v] = cnt;
}

void dfs1(int u, int f, int t){

    bel[u] = t;
    mn[u][0] = mn[u][1] = mn[u][2] = inf;
    pmn[u][0] = pmn[u][1] = pmn[u][2] = 0;
    for(int i = head[u]; i; i = edge[i].nxt){
        
        int v = edge[i].v;
        if(v == f) continue;
        dfs1(v, u, f ? t : v);
        int tmp = min(v, mn[v][0]);
        if(tmp < mn[u][0]){

            mn[u][2] = mn[u][1], pmn[u][2] = pmn[u][1];
            mn[u][1] = mn[u][0], pmn[u][1] = pmn[u][0];
            mn[u][0] = tmp, pmn[u][0] = v;
        }
        else if(tmp < mn[u][1]){

            mn[u][2] = mn[u][1], pmn[u][2] = pmn[u][1];
            mn[u][1] = tmp, pmn[u][1] = v;
        }
        else if(tmp < mn[u][2]){

            mn[u][2] = tmp, pmn[u][2] = v;
        }
    }
}

void dfs2(int u, int f){

    for(int i = head[u]; i; i = edge[i].nxt){

        int v = edge[i].v;
        if(v == f) continue;
        if(v == pmn[u][0]){

            fmn[v] = min(fmn[u], mn[u][1]);
        }
        else{

            fmn[v] = min(fmn[u], mn[u][0]);
        }
        dfs2(v, u);
    }
}

const int maxs = 1e3 + 5;
char buf[maxs], *p1 = buf, *p2 = buf;
inline char fr(){

    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, maxs, stdin)) == p1 ? -1 : *p1++;
}
#define gc fr()
inline int read(int &x){

    char ch; while(!isdigit(ch = gc)) if(ch == -1) return 0; x = ch ^ 48;
    while(isdigit(ch = gc)) x = x * 10 + (ch ^ 48); return 1;
}

int main(){
 
    // ios::sync_with_stdio(0); cin.tie(0);
    while(read(n)){

        read(q);
        cnt = 0;
        for(int i = 1; i <= n; ++i){

            head[i] = 0;
        }
        for(int i = 1; i < n; ++i){

            int u, v; read(u), read(v);
            add(u, v);
        }
        dfs1(1, 0, 0);
        fmn[1] = inf;
        for(int i = head[1]; i; i = edge[i].nxt){

            int v = edge[i].v;
            fmn[v] = inf;
            dfs2(v, 1);
        }
        int ret = 0;
        while(q--){

            int u, v; read(u), read(v);
            u ^= ret, v ^= ret;
            if(bel[u] && bel[v] && bel[u] == bel[v]) ret = 1;
            else{

                int tmp = inf;
                if(u > v) swap(u, v);
                if(v == 1) tmp = 2;
                else if(u == 1){

                    tmp = min(fmn[v], mn[v][0]);
                    if(bel[v] == pmn[1][0]) tmp = min(tmp, mn[1][1]);
                    else tmp = min(tmp, mn[1][0]);
                }
                else{

                    tmp = min(min(fmn[u], mn[u][0]), min(fmn[v], mn[v][0]));
                    for(int i = 0; i < 3; ++i){

                        if(pmn[1][i] != bel[u] && pmn[1][i] != bel[v]){

                            tmp = min(tmp, mn[1][i]);
                            break;
                        }
                    }
                }
                ret = tmp;
            }
            printf("%d\n", ret);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值