10.5NOIP模拟考 dfs序+贪心

解法:

40%:序列上的区间覆盖问题(没有想到……)
100%:
求出每个询问中uv的lca,再按照lca的深度从深到浅排序。越深的lca越优先选择。已选择了的lca在线段树上标记其子树。在第i个询问时检查uv所在的子树有无被标记,若被标记则跳过,没被标记则选择此次uv的lca,并标记其子树,ans++

感性理解

感性理解一下这个贪心的正确性:
首先一条链上选lca是最优的,因为如果这条链被经过了,那它的lca一定会被经过。
为什么又按照lca从深到浅来排呢 ?也就是两条链相交为什么选深的那个lca:
如果两条链相交了,那么它们一定都经过了lca更深的那个点,但是lca更浅的那个点它们却没有都经过。也就是说如果两条链并没有在lca更深的那个点有交,那更不可能在lca更浅的那个点相交了。
所以优先选lca更深的那个点。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int P=16,N=1e5+5;
int n,m;
int anc[N][P+1],in[N],out[N],idc,dep[N],ans;
int to[2*N],nxt[2*N],head[N],etot;
struct data{
    int u,v,lca;
}a[N];
struct node{
    int vsum,fg;
    node *ls,*rs;
    void pushdown(int lf,int rg){
        if(fg){
            int mid=(lf+rg)>>1;
            ls->fg=fg;
            rs->fg=fg;
            ls->vsum=mid-lf+1;
            rs->vsum=rg-mid;
            fg=0;
        }
    }
}pool[2*N+5],*tail=pool,*rt;
void adde(int u,int v)
{
    to[++etot]=v;
    nxt[etot]=head[u];
    head[u]=etot;
}
void dfs(int u,int fa)
{
    in[u]=++idc;
    dep[u]=dep[fa]+1;
    anc[u][0]=fa;
    for(int p=1;p<=P;p++)
    anc[u][p]=anc[anc[u][p-1]][p-1];
    for(int i=head[u];i;i=nxt[i]){
        int v=to[i];
        if(v==fa) continue;
        dfs(v,u);
    }
    out[u]=idc;
}
int lca(int u,int v)
{
    if(dep[u]<dep[v]) swap(u,v);
    int t=dep[u]-dep[v];
    for(int p=0;t;t>>=1,p++)
    if(t&1) u=anc[u][p];
    if(u==v) return u;
    for(int p=P;p>=0;p--)
    if(anc[u][p]!=anc[v][p]) 
    u=anc[u][p],v=anc[v][p];
    return anc[u][0];
}
int cmp(data a,data b){
    return dep[a.lca]>dep[b.lca];
}
node *build(int lf,int rg)
{
    node *nd=++tail;
    if(lf==rg){
        nd->fg=0;
        nd->vsum=0;
        return nd;
    }
    int mid=(lf+rg)>>1;
    nd->ls=build(lf,mid);
    nd->rs=build(mid+1,rg);
    nd->fg=0;
    nd->vsum=0;
    return nd;
}
bool query(node *nd,int lf,int rg,int pos)
{
    if(nd->vsum==rg-lf+1) return 1;
    if(lf==rg) return nd->vsum;
    int mid=(lf+rg)>>1;
    nd->pushdown(lf,rg);
    bool a;
    if(pos<=mid) a=query(nd->ls,lf,mid,pos);
    else  a=query(nd->rs,mid+1,rg,pos);
    nd->vsum=nd->ls->vsum+nd->rs->vsum;
    return a;
}
void modify(node *nd,int L,int R,int lf,int rg)
{
    if(L<=lf&&rg<=R){
        nd->vsum=rg-lf+1;
        nd->fg=1;
        return;
    }
    int mid=(lf+rg)>>1;
    nd->pushdown(lf,rg);
    if(L<=mid) modify(nd->ls,L,R,lf,mid);
    if(R>mid) modify(nd->rs,L,R,mid+1,rg);
    nd->vsum=nd->ls->vsum+nd->rs->vsum;
} 
void init()
{
    etot=0;idc=0;ans=0;
    tail=pool;
    memset(in,0,sizeof(in));
    memset(head,0,sizeof(head));
    memset(anc,0,sizeof(anc));
    memset(dep,0,sizeof(dep));
}
int main()
{
    freopen("fill.in","r",stdin);
    freopen("fill.out","w",stdout);
    int T;
    scanf("%d",&T);
    while(T--){
        init();
        scanf("%d%d",&n,&m);
        for(int i=1;i<n;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            adde(u,v),adde(v,u);
        }
        dfs(1,1);
        for(int i=1;i<=m;i++){
            scanf("%d%d",&a[i].u,&a[i].v);
            a[i].lca=lca(a[i].u,a[i].v);
        }
        sort(a+1,a+1+m,cmp);
        rt=build(1,n);
        for(int i=1;i<=m;i++){
            if(!query(rt,1,n,in[a[i].u])&&!query(rt,1,n,in[a[i].v])){
                ans++;
                modify(rt,in[a[i].lca],out[a[i].lca],1,n);
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值