[CC-ADJLEAF2]Adjacent Leaves

本文探讨了一种树形动态规划(DP)算法,用于解决特定类型的树结构问题,特别是涉及叶子节点集合合法性的判断。通过定义不满子树的概念,提出了一种(mathcalO(nm))的初始解决方案,并进一步优化至(mathcalO(sum|S|logn))的时间复杂度,利用虚树技术减少DFS次数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[CC-ADJLEAF2]Adjacent Leaves

题目大意:

给定一棵有根树,考虑从根开始进行DFS,将所有叶子按照被遍历到的顺序排列得到一个序列。

定义一个叶子集合合法,当且仅当存在一种DFS的方式使得这个叶子集合在序列中的出现位置是一个连续子串。
给出一个\(n(n\le5\times10^5)\)个点的无根树,\(m(m\le5\times10^5)\)次询问,求以\(R\)为根的情况下叶子集合\(S(\sum|S|\le5\times10^5)\)是否合法。

思路:

定义\(leaf[x]\)为以\(x\)为根的子树内叶子结点的个数,\(cnt[x]\)为以\(x\)为根的子树内属于\(S\)的点的个数。

定义以\(x\)为根的子树是“不满的”,当且仅当\(cnt[x]\ne0\)\(cnt[x]\ne leaf[x]\)

对于一个结点\(x\),若其有\(\ge3\)个子树是不满的,或其有\(2\)个子树不满,且\(cnt[x]\ne|S|\),则\(S\)不合法。

对于每次询问,用一次\(\mathcal O(n)\)的树形DP求解,我们就得到了一个\(\mathcal O(nm)\)的做法。

由于\(\sum|S|\le5\times10^5\),我们可以建立虚树,将每次DFS的点数缩小为\(|S|\),此时一次DP的时间复杂度为\(\mathcal O(|S|\log n)\),总时间复杂度\(\mathcal O(\sum|S|\log n)\)

源代码:

#include<cstdio>
#include<cctype>
#include<vector>
#include<algorithm>
inline int getint() {
    register char ch;
    while(!isdigit(ch=getchar()));
    register int x=ch^'0';
    while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
    return x;
}
const int N=5e5+1,logN=19;
std::vector<int> e[N],node;
inline void add_edge(const int &u,const int &v) {
    e[u].push_back(v);
    e[v].push_back(u);
}
int r,dep[N],anc[N][logN],dfn[N],s[N],leaf[N],tot;
inline int lg2(const float &x) {
    return ((unsigned&)x>>23&255)-127;
}
void dfs(const int &x,const int &par) {
    anc[x][0]=par;
    dfn[x]=++tot;
    dep[x]=dep[par]+1;
    leaf[x]=e[x].size()==1;
    for(register int i=1;i<=lg2(dep[x]);i++) {
        anc[x][i]=anc[anc[x][i-1]][i-1];
    }
    for(auto &y:e[x]) {
        if(y==par) continue;
        dfs(y,x);
        leaf[x]+=leaf[y];
    }
}
inline int lca(int x,int y) {
    if(dep[x]<dep[y]) std::swap(x,y);
    for(register int i=lg2(dep[x]-dep[y]);i>=0;i--) {
        if(dep[anc[x][i]]>=dep[y]) {
            x=anc[x][i];
        }
    }
    for(register int i=lg2(dep[x]);i>=0;i--) {
        if(anc[x][i]!=anc[y][i]) {
            x=anc[x][i];
            y=anc[y][i];
        }
    }
    return x==y?x:anc[x][0];
}
inline bool is_anc(const int &x,int y) {
    //x shi y de zuxian
    if(dep[x]>dep[y]) return false;
    for(register int i=lg2(dep[y]-dep[x]);i>=0;i--) {
        if(dep[anc[y][i]]>=dep[x]) {
            y=anc[y][i];
        }
    }
    return x==y;
}
inline int jump(int x,const int &d) {
    //zhaodao x shendu wei d de zuxian
    for(register int i=lg2(dep[x]-d);i>=0;i--) {
        if(dep[anc[x][i]]>=d) {
            x=anc[x][i];
        }
    }
    return x;
}
inline int near_ch(const int &x,const int &y) {
    //zhaodao x zuijin de zijiedian, bingqie shi y de zuxian
    if(is_anc(x,y)) {
        return jump(y,dep[x]+1);
    } else {
        return anc[x][0];
    }
}
inline int count_leaves(const int &x) {
    if(x==r) return leaf[1];
    if(is_anc(x,r)) {
        return leaf[1]-leaf[jump(r,dep[x]+1)];
    } else {
        return leaf[x];
    }
}
class AuxTree {
    //auxiliary tree
    private:
        int stk[N],top,cnt[N];
        std::vector<int> e[N];
        void add_edge(const int &u,const int &v) {
            e[u].push_back(v);
            e[v].push_back(u);
        }
    public:
        void build(int s[],const int &r) {
            const int n=s[0]+1;
            s[n]=r;
            std::sort(&s[1],&s[n]+1,
                [](const int &i,const int &j) {
                    return dfn[i]<dfn[j];
                }
            );
            stk[top=1]=s[1];
            node.push_back(s[1]);
            for(register int i=2;i<=n;i++) {
                const int &x=s[i],p=lca(x,stk[top]);
                while(dfn[p]<dfn[stk[top]]) {
                    if(dfn[p]>=dfn[stk[top-1]]) {
                        add_edge(p,stk[top--]);
                        if(stk[top]!=p) {
                            stk[++top]=p;
                            node.push_back(p);
                        }
                        break;
                    } else {
                        add_edge(stk[top],stk[top-1]);
                        top--;
                    }
                }
                stk[++top]=x;
                node.push_back(x);
            }
            for(;top>1;top--) {
                add_edge(stk[top],stk[top-1]);
            }
        }
        void dfs(const int &x,const int &par) {
            int tmp=0;
            cnt[x]=x!=r&&e[x].size()==1;
            for(auto &y:e[x]) {
                if(y==par) continue;
                dfs(y,x);
                cnt[x]+=cnt[y];
                tmp+=cnt[y]<count_leaves(near_ch(x,y));
            }
            if(tmp>2||(tmp==2&&cnt[x]!=s[0])) throw 0;
        }
        void clear() {
            for(auto &x:node) {
                e[x].clear();
            }
            node.clear();
        }
};
AuxTree t;
int main() {
    const int n=getint(),m=getint();
    for(register int i=1;i<n;i++) {
        add_edge(getint(),getint());
    }
    dfs(1,0);
    for(register int i=0;i<m;i++) {
        r=getint();
        for(register int i=0;i<=s[0];i++) {
            s[i]=getint();
        }
        t.build(s,r);
        try {
            t.dfs(r,0);
            puts("YES");
        } catch(...) {
            puts("NO");
        }
        t.clear();
    }
    return 0;
}

转载于:https://www.cnblogs.com/skylee03/p/10186771.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值