Codeforce gym 101741 problem C Cover the Paths 树链剖分或者set乱搞

点我看题
18.11.14 update:
PS 四个月之后再来看这道题,发现不会写了,感觉很真实
题意 给出一颗树,然后给出m条简单路径,定义一个集合S为好集合 当且仅当对于每一条简单路径 ,都至少有一个点包含在S内。
现在要你求出大小最小的S集合 。
首先就应该往LCA的方向上去想。 将树变成一颗有向树,从最深层的LCA开始考虑。
这里要判断一条路径上是否有点已经被选取,可以用树链剖分来写。

#include<iostream>
#include<cstdio>
#include <algorithm>
#include <string.h>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
const int MAX=1e5+10;
int sum[MAX<<2];
void push_up(int rt){
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void update(int p,int l,int r,int rt){
    if(l==r){
        sum[rt]++;
        return;
    }
    int m=(l+r)>>1;
    if(m>=p){
        update(p,lson);
    }else{
        update(p,rson);
    }
    push_up(rt);
}
int query(int L,int R,int l,int r,int rt){
    if(l>=L && r<=R){
        return sum[rt];
    }
    int ans=0;
    int m=(l+r)>>1;
    if(m>=L){
        ans+=query(L,R,lson);
    }
    if(m<R){
        ans+=query(L,R,rson);
    }
    return ans;
}
class Edge{
public:
    int u,v,next;
};
int tot;
int head[MAX];
Edge edge[MAX<<1];
void add(int u,int v){
    edge[tot].u=u;
    edge[tot].v=v;
    edge[tot].next=head[u];
    head[u]=tot;
    tot++;
}
int a[MAX<<2];
int fa[MAX],top[MAX],siz[MAX],son[MAX],dep[MAX],id[MAX],rear;
void dfs1(int u,int f,int d){
    fa[u]=f,dep[u]=d;
    son[u]=0;
    siz[u]=1;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        if(v==f){
            continue;
        }
        dfs1(v,u,d+1);
        siz[u]+=siz[v];
        if(siz[son[u]]<siz[v]){
            son[u]=v;
        }
    }
}
void dfs2(int u,int tp){
    top[u] = tp;
    id[u]= ++rear;
    if(son[u]) dfs2(son[u],tp);
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        if(v==fa[u]|| v==son[u]) continue;
        dfs2(v,v);
    }
}
void presolve(){
    rear = 0;
    dfs1(1,0,1);
    dfs2(1,1);
}
void HDL_update(int x){
    update(id[x],1,rear,1);
}
int HDL_query(int u,int v){
    int tp1= top[u];
    int tp2 = top[v];
    int sum= 0;
    while(tp1!=tp2){
        if(dep[tp1]<dep[tp2]){
            swap(u,v);
            swap(tp1,tp2);
        }
        sum+=query(id[tp1],id[u],1,rear,1);
        u=fa[tp1];
        tp1=top[u];
    }
    if(dep[u]>dep[v]) swap(u,v);
    sum+=query(id[u],id[v],1,rear,1);
    return sum;
}
int lca(int u,int v){
    int tp1=top[u],tp2=top[v];
    while(tp1!=tp2){
        if(dep[tp1]<dep[tp2]){
            swap(u,v);
            swap(tp1,tp2);
        }
        u=fa[tp1];
        tp1=top[u];
    }
    return dep[u]>dep[v]? v:u;
}
class Node{
public:
    int lca,a,b;
    bool operator < (const Node &b)const{
        return dep[lca]>dep[b.lca];
    }
};
int main(){
    memset(head,-1,sizeof head);
    int n;
    scanf("%d", &n);
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d %d",&u,&v);
        add(u,v);
        add(v,u);
    }
    presolve();
    vector<Node> V;
    int m;
    scanf("%d",&m);
    for(int i=1;i<=m;i++){
        Node cnt;
        scanf("%d %d",&cnt.a,&cnt.b);
        cnt.lca=lca(cnt.a,cnt.b);
        V.push_back(cnt);
    }
    vector<int> ans;
    sort(V.begin(),V.end());
    for(int i=0;i<V.size();i++){
        Node cnt = V[i];
        if(HDL_query(cnt.a,cnt.b)>0) continue;
        ans.push_back(cnt.lca);
        HDL_update(cnt.lca);
    }
    cout<<ans.size()<<endl;
    for(int i=0;i<ans.size();i++){
        cout<<ans[i]<<" ";
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值