牛客练习赛72 F brz的树

brz的树

https://ac.nowcoder.com/acm/contest/8282/F

这场题好妙啊,不仅打开我的凸包新大门,这两题树也真是妙阿 brz, yyds!

题意:

在一颗点被染色的带根树上询问仅出现在两颗子树中的颜色数

题解:

颜色数有两种贡献,一种仅在单颗子树中出现的颜色,和仅在两颗子树中出现的颜色

两颗子树也有两种情况,一种是包含关系,对于这种直接计算第一种贡献即可

第二种两颗子树的情况,先计算第一颗和第二颗的第一种贡献相加

考虑第二种贡献,设a和b为保证颜色C仅在其子树中存在的dfs序最大的两个节点

容易想到,要么存在这两个节点,要么颜色C对第二种没有贡献

然后是极妙的地方了, 对颜色C求虚树,直接看根节点有没有两个儿子即可。同时对所有儿子求lca,可得到颜色C仅在其子树中出现的dfs序最大的点,记录一下即可

最后计算贡献时,对于ab链,经过a点时,将b点的值加一,对询问点的子树求和即可计算出第二种贡献

完整代码

struct BitTree {
    int res[Ma];
    void add(int pos, int val) {for (; pos < Ma; pos += lowbit(pos)) res[pos] += val;}
    int sum(int pos) {int s = 0; for (; pos; pos -= lowbit(pos)) s += res[pos]; return s;}
    int range(int l, int r) {return sum(r - 1) - sum(l - 1);}
};

int c[Ma];
vector<int> val[Ma];
int ans[Ma];
typedef pair<int, int*> ask_t;
vector<ask_t> ak[Ma];

template<typename T>
struct HLD {
    BitTree bt;
    static const int Ma = 1e5 + 100;
    int fa[Ma], dep[Ma], siz[Ma], son[Ma], st[Ma];
    int top[Ma], dfn[Ma], rnk[Ma]; int cnt;
    typedef std::vector<std::vector<T>> Tree;
    const std::vector<std::vector<T> >& g;

    void dfs1(int u) {
        son[u] = -1; siz[u] = 1;
        for (const auto& i : g[u]) if (!dep[i]) {
            dep[i] = dep[u] + 1;
            fa[i] = u;
            dfs1(i);
            siz[u] += siz[i];
            if (son[u] == -1 or siz[i] > siz[son[u]]) son[u] = i;
        }
    }
    void dfs2(int u, int f) {
        top[u] = f; dfn[u] = ++cnt; rnk[cnt] = u;
        if (!~son[u]) return ;
        dfs2(son[u], f);
        for (const auto& i : g[u]) if (i != son[u] and i != fa[u])
            dfs2(i, i);
    }
    HLD(std::vector<std::vector<T> >& gg) : g(gg) {
        memset(dep, 0, sizeof dep);
        dep[0] = 1; cnt = 0;
        dfs1(0); dfs2(0, 0);
    }
    int LCA(int u, int v) const {
        while (top[u] != top[v]) {
            if (dep[top[u]] > dep[top[v]]) u = fa[top[u]];
            else v = fa[top[v]];
        }
        return dep[u] > dep[v] ? v : u;
    }

    void solve(int u, int fa) {
        rap (i, ak[u]) *i.S -= bt.range(dfn[i.F], dfn[i.F] + siz[i.F]);
        rap (i, val[u]) bt.add(dfn[i], 1);
        if (~son[u]) solve(son[u], u);
        rap (i, g[u]) if (i != fa and i != son[u]) solve(i, u);
        rap (i, ak[u]) *i.S += bt.range(dfn[i.F], dfn[i.F] + siz[i.F]);
    }

    Tree& vtree(std::vector<int>& key) {
        static Tree tr(cnt); int top = 0; st[top] = 0; tr[0].clear();
        cc_hash_table<int, null_type> vis;
        std::sort(key.begin(), key.end(), [&](int a, int b) {return dfn[a] < dfn[b];});
        static std::function<void(int, int)> add = [&] (const int& u, const int& v) {
            if (vis.find(u) == vis.end()) tr[u].clear(), vis.insert(u);
            tr[u].emplace_back(v);
        };
        static std::function<void(int)> insert = [&] (int x) {
            if (top == 0) {x != 0 ? st[++top] = x : 0; return ;}
            int lca = LCA(x, st[top]);
            if (lca == st[top]) return ; // st[++top] = x;
            else {
                while (top > 0 and dfn[st[top - 1]] >= dfn[lca])
                    add(st[top - 1], st[top]), --top;
                if (lca != st[top]) add(lca, st[top]), st[top] = lca;
                st[++top] = x;
            }
        };
        for (const auto& i : key) insert(i);
        while (top > 0) add(st[top - 1], st[top]), --top;
        return tr;
    }
};

int cc[Ma];
int own[Ma];
vector< vector<int> > g;
vector<int> vp[Ma];

void cal(int u, int fa) {
    rap (i, g[u]) if (i != fa) cal(i, u);
    if (u) own[fa] += own[u];
}

signed main() {
    #if SYNC==0
    ios::sync_with_stdio(false);
    cin.tie(0);
    #endif
    int n, m; cin >> n >> m;
    rep (i, n) {
        cin >> c[i];
        vp[c[i]].ep(i);
    }
    memcpy(cc, c, sizeof c);
    sort(cc, cc + n);
    int len = unique(cc, cc + n) - cc;
    g.resize(n);
    rep (i, n - 1) {
        int u, v; cin >> u >> v; --u, --v;
        g[u].ep(v); g[v].ep(u);
    }
    static HLD<int> hld(g);
    rep (i, len) {
        vector< vector<int> >& tp = hld.vtree(vp[cc[i]]);
        int aim = 0;
        if (tp[0].size() == 1 and c[tp[0][0]] != cc[i]) aim = tp[0][0];
        debug(cc[i], aim, tp[aim].size());
        if (cc[i] != c[0] and tp[aim].size() == 2) {
            int a = tp[aim][0], b = tp[aim][1];
            debug(a, b);
            if (hld.dfn[a] > hld.dfn[b]) swap(a, b);
            val[a].ep(b);
        }
        if (cc[i] == c[0] or tp.empty()) ++own[0];
        else {
            int now = tp[0][0];
            for (size_t i = 1; i < tp[0].size(); i++) now = hld.LCA(now, tp[0][i]);
            ++own[now];
        }
    }
    cal(0, 0);
    debug(own[1], own[2]);
    rep (i, m) {
        int x, y; cin >> x >> y; --x, --y;
        if (hld.dfn[x] > hld.dfn[y]) swap(x, y);
        int lca = hld.LCA(x, y);
        if (lca == x) {ans[i] = own[x]; continue;}
        else ans[i] = own[x] + own[y];
        ak[x].ep(mkp(y, ans + i));
    }
    hld.solve(0, 0);
    rep (i, m) cout << ans[i] << endl;

    return 0;
}
全部评论

相关推荐

03-19 18:27
已编辑
门头沟学院 C++
26学院本太难了,很多公司机筛就给我刷了。机会都难拿到如果是简历存在问题也欢迎拷打————————————————————分割线——————————————————————2026.3.4更新:发完贴之后,时不时投递又收到了不少的笔试/面试邀请。主要是之前投递简历出去之后基本上都是沉默状态,年后好转了不少timeline:2026.01.21&nbsp;文远知行笔试,半年多没刷算法题&nbsp;-&gt;挂&nbsp;(后续HR说春招可以重新安排笔试)2026.2.4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;小鹏汇天&nbsp;技术一面,第二周收到结果&nbsp;-&gt;挂2026.2.12&nbsp;&nbsp;&nbsp;大众Cariad代招&nbsp;技术二面&nbsp;-&gt;Offer2026.2.28&nbsp;&nbsp;&nbsp;多益网络技术面试,由于风评太差,一直在犹豫要不要接面试&nbsp;-&gt;推迟-----------分割线-----------2026.3&nbsp;月前的某一天,临时去电网报名了二批计算机岗位的笔试2026.3.6&nbsp;从上家公司实习离职,氛围最好的一家公司,leader&nbsp;说可以帮忙转正,但是流程太长,而且我们部门据说只有一个&nbsp;hc,更想要研究生,我很有可能是会被签外包公司在这里干活,就离职了。2026.3.9&nbsp;入职新公司,大众Cariad&nbsp;以外部公司的身份进组,项目组签了三年,后续三年应该都可以在这里呆,不知道有没有希望原地跳槽。2026.3.10&nbsp;电网考试居然说我通过资格审查了,短信约我去参加资格审查,请假一天,买了&nbsp;12&nbsp;号晚上的机票回成都2026.3.15&nbsp;参加国家电网三新计算机类的笔试2026.3.17&nbsp;电网出成绩了,感觉很低。觉得已经🈚️了2026.3.18&nbsp;收到电网面试通知,通知&nbsp;3.22-3.25&nbsp;这个时间去面试,我的岗位只招&nbsp;1&nbsp;个人。据说面试只有&nbsp;2-3&nbsp;人,不知道能不能成功
点赞 评论 收藏
分享
点赞 评论 收藏
分享
评论
2
收藏
分享

创作者周榜

更多
正在热议
更多
# 春招至今,你的战绩如何? #
4734次浏览 44人参与
# 你的实习产出是真实的还是包装的? #
1093次浏览 27人参与
# 米连集团26产品管培生项目 #
4020次浏览 193人参与
# 军工所铁饭碗 vs 互联网高薪资,你会选谁 #
6891次浏览 36人参与
# 简历第一个项目做什么 #
31243次浏览 312人参与
# 当下环境,你会继续卷互联网,还是看其他行业机会 #
186328次浏览 1114人参与
# MiniMax求职进展汇总 #
22842次浏览 293人参与
# 面试紧张时你会有什么表现? #
30317次浏览 188人参与
# 简历中的项目经历要怎么写? #
309349次浏览 4149人参与
# 网易游戏笔试 #
6302次浏览 83人参与
# 职能管理面试记录 #
10676次浏览 59人参与
# 把自己当AI,现在最消耗你token的问题是什么? #
6843次浏览 154人参与
# 从哪些方向判断这个offer值不值得去? #
56694次浏览 357人参与
# 腾讯音乐求职进展汇总 #
160388次浏览 1105人参与
# 小红书求职进展汇总 #
226842次浏览 1356人参与
# AI时代,哪些岗位最容易被淘汰 #
62336次浏览 725人参与
# 你怎么看待AI面试 #
179242次浏览 1162人参与
# 正在春招的你,也参与了去年秋招吗? #
362478次浏览 2631人参与
# 你的房租占工资的比例是多少? #
92122次浏览 896人参与
# 机械求职避坑tips #
94395次浏览 567人参与
# 校招笔试 #
466042次浏览 2950人参与
# 面试官最爱问的 AI 问题是...... #
27078次浏览 834人参与
牛客网
牛客网在线编程
牛客网题解
牛客企业服务