HDU6203 补题LCA复习+dfs序

本文介绍了一个使用贪心算法解决特定LCA(最近公共祖先)问题的方法。通过构建树结构并采用深度优先搜索(DFS),实现了一种新的LCA求解策略。该方法首先确定每对节点的最近公共祖先,再根据深度排序并逐步移除节点以找到最优解。

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

这个题目其实算是贪心吧,感觉自己贪心有点菜,这几天要把贪心练一下了

建树,dfs什么的就不说了,这里主要讲下思路

就是建完树后,给你两个点

然后求出他们的lca,将这两个点u,v和lca,dlca(lca点的深度)存在一个结构体数组中

然后将这个数组按深度从大到小排序

接着遍历这个数组

每次看u,v有没有访问过,如果有就不管,如果没有,就将他们的lca点拿掉并将lca点的所有子节点记为访问过并且ans++

最后输出ans就好了

其实,为什么这样做可以呢

对于一个u,v,我们要做的就是把他们分开,我们就可以拿掉链接u,v上的点的任意一个就好,那么就会出现一个问题

如果有另外两个点的lca正好经过这一条链,那么如果我只拿掉那个点的lca就好了

如果我们这样排序的话,其实就保证了这种情况,也就是保证了最优

还有一点,我们标记已访问过的点时,不需要用到树链剖分,因为是对子树的操作,dfs序就可以了

这个题目有点手残,用的线段树求的lca,代码量直接爆炸了


#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 60010

using namespace std;

struct Edge
{
    int next,en;
};



struct Save
{
    int node,dep;
    Save(int n,int d){node=n;dep=d;}
    Save(){}
};

bool operator < (Save a,Save b)
{return a.dep<b.dep;}

struct Node
{
    int u,v;
    Save lca;
};
bool operator < (Node a,Node b)
{return a.lca.dep<b.lca.dep;}

bool cmp(Node a,Node b)
{return a.lca.dep>b.lca.dep;}

Edge edge[maxn];
Node node[maxn];
Save save[maxn*2];
Save segt[maxn<<2];//线段树的大小为节点大小的4倍

int head[maxn],cnte;
int dfsx,in[maxn],out[maxn],vis[maxn],cntn;
int LCAArray[maxn*2];



void init()
{
    memset(head,-1,sizeof(head));
    memset(vis,0,sizeof(vis));
    cnte=0;cntn=0;dfsx=0;
}

void addedge(int st,int en)
{
    cnte++;
    edge[cnte].en=en;
    edge[cnte].next=head[st];
    head[st]=cnte;
    return;
}

void addnode(int u,int v,Save lca)
{
    cntn++;
    node[cntn].u=u;node[cntn].v=v;
    node[cntn].lca=lca;
    return;
}

void dfs(int root,int depth)//dfs没问题
{
    dfsx++;
    in[root]=dfsx;vis[root]=1;
    save[dfsx].dep=depth;save[dfsx].node=root;

    for (int k=head[root];k!=-1;k=edge[k].next)
        if (!vis[edge[k].en])
        {
            dfs(edge[k].en,depth+1);
            dfsx++;
            save[dfsx].dep=depth;save[dfsx].node=root;
        }
    out[root]=dfsx;
    return;
}

void build(int root,int beg,int en)//线段树建树函数
{
    if (beg==en)
        segt[root]=save[beg];
    else
    {
        build(root*2,beg,(beg+en)/2);
        build(root*2+1,(beg+en)/2+1,en);
        if (segt[root*2]<segt[root*2+1])
            segt[root]=segt[root*2];
        else segt[root]=segt[root*2+1];
    }
    return;
}

Save query(int root,int l,int r,int ql,int qr)
{

    if(l == ql && r == qr) return segt[root];

    int mid=(l+r)/2;

    if(qr <= mid) return query(root*2,l,mid,ql,qr);
    else if(ql >= mid + 1) return query(root*2+1,mid+1,r,ql,qr);

    return min(query(root*2,l,mid,ql,mid), query(root*2+1,mid + 1,r,mid + 1,qr));
}

void initLCA(int n)
{
    dfs(0,1);
    build(1,1,dfsx);
}

Save LCA(int u,int v)
{
    int qbeg,qend;
    if (in[u]<in[v])
    {qbeg=in[u];qend=in[v];}
    else
    {qbeg=in[v];qend=in[u];}

    return query(1,1,dfsx,qbeg,qend);
}


struct Segt
{
    int val,addmark;
};

Segt segtf[maxn<<2];

void pushdown(int root)
{
    if (segtf[root].addmark!=0)
    {
        segtf[root*2].val+=segtf[root].addmark;
        segtf[root*2].addmark+=segtf[root].addmark;
        segtf[root*2+1].val+=segtf[root].addmark;
        segtf[root*2+1].addmark+=segtf[root].addmark;
        segtf[root].addmark=0;
    }
    return;
}

int queryf(int root,int l,int r,int poi)
{

    if(l==r) return segtf[root].val;
    pushdown(root);
    int mid=(l+r)/2;

    if(poi <= mid) return queryf(root*2,l,mid,poi);
    else if(poi >= mid + 1) return queryf(root*2+1,mid+1,r,poi);
}

void updatef(int root,int rst,int ren,int beg,int en,int val)
{
    if (rst>=beg&&ren<=en)
    {
        segtf[root].val+=val;
        segtf[root].addmark+=val;
    }
    else if (rst>en||ren<beg) {return;}
    else
    {
        pushdown(root);
        int mid=(rst+ren)/2;
        updatef(root*2,rst,mid,beg,en,val);
        updatef(root*2+1,mid+1,ren,beg,en,val);
        segtf[root].val=segtf[root*2].val&&segtf[root*2+1].val;
    }
}

int main()
{
    int n,m;
    while(~scanf("%d",&n))
    {
        init();
        for (int k=1;k<=n;k++)
        {
            int st,en;
            scanf("%d %d",&st,&en);
            addedge(st,en);addedge(en,st);
        }

        n++;
        initLCA(n);

        scanf("%d",&m);
        for (int k=1;k<=m;k++)
        {
            int u,v;
            Save lca;
            scanf("%d %d",&u,&v);
            lca=LCA(u,v);
            addnode(u,v,lca);
        }

        sort(node+1,node+1+cntn,cmp);


        memset(segtf,0,sizeof(segtf));

        int ans(0);
        for (int k=1;k<=cntn;k++)
        {
            int u,v,l;
            u=node[k].u;v=node[k].v;l=node[k].lca.node;
            if ((queryf(1,1,dfsx,in[u])==0)&&(queryf(1,1,dfsx,in[v])==0))
            {
                ans++;
                updatef(1,1,dfsx,in[l],out[l],1);
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值