【NOIP2017提高A组模拟10.5】Ping

30 篇文章 0 订阅
7 篇文章 0 订阅

Description

这里写图片描述

Input

这里写图片描述

Output

这里写图片描述

Sample Input

5 4
2 1
5 3
3 1
4 3
2
2 4
3 2

Sample Output

1
2

Solution

如果是序列,可以知道,选的点一定是尽量靠右,贪心的脑补一下就可以证明了
树上的话就是尽量选LCA,原因是一样的
按照LCA的dfs序反着做,对于每一个限制,如果它上面没有点被选择,就选择这个LCA
实现用链剖,LCT之类的都行
好像还有set加启发式合并?我不会

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define N 101000
using namespace std;
int n,m,top[N],son[N],fa[N],deep[N],last[N],next[N*5],to[N*5],tot=1,size[N],dfn[N],f[N][17],ans[N],t[N*10];
struct node{
    int x,y,z;
}q[N*3];
bool cnt(node x,node y){return dfn[x.z]<dfn[y.z];}
void putin(int x,int y)
{
    next[++tot]=last[x];last[x]=tot;to[tot]=y;
}
void dg1(int x)
{
    for(int i=last[x];i;i=next[i])
    {
        int y=to[i];
        if(deep[y]) continue;
        deep[y]=deep[x]+1;fa[y]=f[y][0]=x;
        dg1(y);
        size[x]+=size[y];
        if(size[y]>size[son[x]]) son[x]=y;
    }
    size[x]++;
}
void dg2(int x)
{
    dfn[x]=++tot;
    if(son[x]) top[son[x]]=top[x],dg2(son[x]);
    for(int i=last[x];i;i=next[i])
    {
        int y=to[i];
        if(top[y]) continue;
        top[y]=y;dg2(y);
    }
}
int lca(int x,int y)
{
    if(deep[x]<deep[y]) swap(x,y);
    fd(i,16,0) if(deep[f[x][i]]>=deep[y]) x=f[x][i];
    fd(i,16,0) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    if(x!=y) x=fa[x];
    return x;
}
void insert(int v,int i,int j,int x)
{
    t[v]++;
    if(i==j) return;
    int mid=(i+j)/2;
    if(x<=mid) insert(v*2,i,mid,x);
    else insert(v*2+1,mid+1,j,x);
}
int get(int v,int i,int j,int x,int y)
{
    if(i==x&&j==y) return t[v];
    int mid=(i+j)/2;
    if(y<=mid) return get(v*2,i,mid,x,y);
    else if(x>mid) return get(v*2+1,mid+1,j,x,y);
         else return get(v*2,i,mid,x,mid)+get(v*2+1,mid+1,j,mid+1,y);
}
int LCT(int x,int y)
{
    int ans=0;
    while(deep[x]>=deep[y])
    {
        int f1=top[x];
        if(deep[f1]<deep[y]) f1=y;
        ans=ans+get(1,1,n,dfn[f1],dfn[x]);
        x=fa[f1];
        if(ans) return ans;
    }
    return ans;
}
int main()
{
    freopen("ping.in","r",stdin);
    freopen("ping.out","w",stdout);
    scanf("%d%d",&n,&n);n++;
    fo(i,1,n-1)
    {
        int x,y;scanf("%d%d",&x,&y);
        putin(x,y);putin(y,x);
    }
    deep[1]=top[1]=1;tot=0;dg1(1);dg2(1);
    fo(j,1,16) fo(i,1,n) f[i][j]=f[f[i][j-1]][j-1];
    scanf("%d",&m);
    fo(i,1,m) scanf("%d%d",&q[i].x,&q[i].y),q[i].z=lca(q[i].x,q[i].y);
    sort(q+1,q+m+1,cnt);
    fd(i,m,1)
    {
        int jy=LCT(q[i].x,q[i].z)+LCT(q[i].y,q[i].z);
        if(jy==0) ans[++ans[0]]=q[i].z,insert(1,1,n,dfn[q[i].z]);
    }
    printf("%d\n",ans[0]);
    fo(i,1,ans[0]) printf("%d ",ans[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值