jzoj5394 【NOIP2017提高A组模拟10.5】Ping

21 篇文章 0 订阅
8 篇文章 0 订阅

题面

这里写图片描述
这里写图片描述

分析

很容易看出模型:有若干条路径,选最少的点使得每条路径上都有点。并给出方案。
考虑一条链,其实就抽象成数轴上的线段。显然是贪心。(然而我最后五分钟才想起来!?

把线段按右端点从小到大排序,考虑右端点最左的一条线段,其他线段要么与他没有交集,要么从右边有交。那么这条线段取点一定是取右端点能覆盖更多的线段。
把覆盖掉的线段删除,重复上述步骤,直到覆盖完毕。
还能得出一个结论: 只选右端点。

对于树上的两条路径,我们知道路径A交路径B,要么A的lca在B上,要么B的lca在A上。
所以,对于一条Lca深度最大的路径,一定在他这选一个点(没有其他路径的lca在他上面)。 自然是选深度最小的lca最好了,这样能覆盖更多上面的路径。
证明嘛感性一点好了C(・ω・)ノ

所以拿个树剖维护下就好了。

Demo

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define lowbit(x) ((x)&-(x))

using namespace std;
const int N = 1e5 + 10;
int n,m,k;
int to[N*2],nex[N*2],final[N],tot;
int dep[N],fa[N],top[N],size[N],hvy[N],loc[N];
int ans[N],t[N];

struct path{
    int x,y,lca;
} s[3*N];
bool cmp(path a,path b) {return dep[a.lca]>dep[b.lca];}
void link(int x,int y) {
    to[++tot]=y,nex[tot]=final[x],final[x]=tot;
}
void dfs(int x) {
    dep[x]=dep[fa[x]]+1;
    size[x]=1;
    for (int i=final[x]; i; i=nex[i]) {
        int y=to[i]; if (y==fa[x]) continue;
        fa[y]=x;
        dfs(y);
        size[x]+=size[y];
        if (size[y]>size[hvy[x]]) hvy[x]=y;
    }
}
int stm;
void build(int x) {
    loc[x]=++stm;
    if (!top[x]) top[x]=x;
    if (hvy[x]) top[hvy[x]]=top[x],build(hvy[x]);
    for (int i=final[x]; i; i=nex[i]) {
        if (to[i]!=fa[x] && to[i]!=hvy[x]) build(to[i]);
    }
}
int lca(int x,int y) {
    while (top[x]!=top[y]) {
        if (dep[top[x]]<dep[top[y]]) swap(x,y);
        x=fa[top[x]];
    }
    return dep[x]>dep[y]?y:x;
}
void change(int x) {
    for (; x<=n; x+=lowbit(x)) t[x]++;
}
int sum(int x) {
    int sum=0;
    for (; x; x-=lowbit(x)) sum+=t[x];
    return sum;
}
bool query(int x,int y) {
    while (top[x]!=top[y]) {
        if (sum(loc[x]) - sum(loc[top[x]]-1) > 0) return 1;
        x=fa[top[x]];
    }
    if (sum(loc[x]) - sum(loc[y]-1) > 0) return 1;
    return 0;
}
int main() {
    freopen("ping.in","r",stdin);
    //freopen("ping.out","w",stdout);
    cin>>n>>m;
    for (int i=1; i<=m; i++) {
        int u,v; scanf("%d %d",&u,&v);
        link(u,v),link(v,u);
    }
    dfs(1);
    build(1);
    cin>>k;
    for (int i=1; i<=k; i++) {
         int x,y; scanf("%d %d",&s[i].x,&s[i].y);
         s[i].lca=lca(s[i].x,s[i].y);
    }
    sort(s+1,s+1+k,cmp);
    for (int i=1; i<=k; i++) {
        if (query(s[i].x,s[i].lca) + query(s[i].y,s[i].lca) == 0) {
            change(loc[s[i].lca]);
            ans[++ans[0]]=s[i].lca;
        }
    }
    cout<<ans[0]<<endl;
    for (int i=1; i<=ans[0]; i++) printf("%d\n",ans[i]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值