UOJ510 【JOISC2020】首都

UOJ510 【JOISC2020】首都

建图

这恐怕是JOISC2020最简单的题目之一了。

对于一个城市的城镇,如果要使它连成连通块,必然需要将该城镇的点所组成的生成树上的所有点都结合在一起。

我们利用建虚树的方式建图,可以利用重链剖分加线段树优化建图,不过倍增优化建图可以少一只\(\log\)

显然,建图后,强连通分量中的所有城市必须合并才有可能满足条件。

于是我们缩点后得到一个拓扑图,由于存在出度的点必须与其指向的点合并,因此最优的方案一定来源于出度为\(0\)的点,直接枚举即可。

由于蒟蒻比较菜,写的是重链剖分加线段树优化建图。

\(Code:\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<stack>
#define N 200005
using namespace std;
int n,k,x,y,c[N];
struct edge
{
    int nxt,v;
    edge () {}
    edge (int xx,int yy):nxt(xx),v(yy) {}
}e[N << 1];
int tot,fr[N];
void add(int x,int y)
{
    ++tot;
    e[tot]=edge(fr[x],y),fr[x]=tot;
}
int sz[N],son[N],fa[N];
int cnt,dep[N],bg[N],lg2[N << 1],st[N << 1][22];
int lc[N];
void dfs(int u)
{
    st[++cnt][0]=u,bg[u]=cnt;
    sz[u]=1;
    int mx(-1);
    for (int i=fr[u];i;i=e[i].nxt)
    {
        int v(e[i].v);
        if (v==fa[u])
            continue;
        fa[v]=u;
        dep[v]=dep[u]+1;
        dfs(v);
        st[++cnt][0]=u;
        sz[u]+=sz[v];
        son[u]=(sz[v]>mx)?(mx=sz[v],v):son[u];
    }
}
int lca(int x,int y)
{
    if (!x || !y)
        return x|y;
    x=bg[x],y=bg[y];
    if (x>y)
        swap(x,y);
    int k(lg2[y-x+1]);
    return (dep[st[x][k]]<=dep[st[y-(1 << k)+1][k]])?st[x][k]:st[y-(1 << k)+1][k];
}
int tt,dfn[N],rdfn[N],t[N];
int ans=1000000007;
void dfs2(int u,int tp)
{
    t[u]=tp,dfn[++tt]=u,rdfn[u]=tt;
    if (!son[u])
        return;
    dfs2(son[u],tp);
    for (int i=fr[u];i;i=e[i].nxt)
    {
        int v(e[i].v);
        if (v==fa[u] || v==son[u])
            continue;
        dfs2(v,v);
    }
}
namespace G
{
    edge e[40000005];
    int tot,fr[N << 3];
    void add(int x,int y)
    {
        ++tot;
        e[tot]=edge(fr[x],y),fr[x]=tot;
    }
};
int crt,cn,Dfn[N << 3],Low[N << 3];
bool insta[N << 3];
void build(int p,int l,int r)
{
    if (l==r)
        return;
    crt=max(crt,p+k);
    int mid(l+r >> 1);
    build(p << 1,l,mid);
    build(p << 1 | 1,mid+1,r);
    if (l==mid)
        G::add(p+k,c[dfn[l]]); else
        G::add(p+k,(p << 1)+k);
    if (mid+1==r)
        G::add(p+k,c[dfn[r]]); else
        G::add(p+k,(p << 1 | 1)+k);
}
void link(int u,int p,int l,int r,int x,int y)
{
    if (l==x && r==y)
    {
        if (l==r)
            G::add(u,c[dfn[l]]); else
            G::add(u,p+k);
        return;
    }
    int mid(l+r >> 1);
    if (y<=mid)
        link(u,p << 1,l,mid,x,y); else
    if (x>mid)
        link(u,p << 1 | 1,mid+1,r,x,y); else
        {
            link(u,p << 1,l,mid,x,mid);
            link(u,p << 1 | 1,mid+1,r,mid+1,y);
        }
}
void modify(int id,int x,int y)
{
    while (t[x]!=t[y])
    {
        link(id,1,1,tt,rdfn[t[x]],rdfn[x]);
        x=fa[t[x]];
    }
    link(id,1,1,tt,rdfn[y],rdfn[x]);
}
int sq,lk,s[N << 3],bel[N << 3],lar[N << 3],cd[N << 3];
void tarjan(int u)
{
    s[++sq]=u;
    Dfn[u]=Low[u]=++cn;
    insta[u]=true;
    for (int i=G::fr[u];i;i=G::e[i].nxt)
    {
        int v(G::e[i].v);
        if (!Dfn[v])
            tarjan(v),Low[u]=min(Low[u],Low[v]); else
        if (insta[v])
            Low[u]=min(Low[u],Dfn[v]);
    }
    if (Dfn[u]==Low[u])
    {
        ++lk;
        do
        {
            int v(s[sq]);
            bel[v]=lk;
            insta[v]=false;
            if (v<=k)
                ++lar[lk];
            --sq;
        } while (s[sq+1]!=u);
    }
}
void topo()
{
    for (int i=1;i<=crt;++i)
        for (int j=G::fr[i];j;j=G::e[j].nxt)
        {
            int u(i),v(G::e[j].v);
            if (bel[u]==bel[v])
                continue;
            ++cd[bel[u]];
       }
    for (int i=1;i<=lk;++i)
        if (lar[i] && !cd[i])
            ans=min(ans,lar[i]);
}
int main()
{
    scanf("%d%d",&n,&k);
    for (int i=1;i<n;++i)
    {
        scanf("%d%d",&x,&y);
        add(x,y),add(y,x);
    }
    dep[1]=1,dfs(1);
    lg2[0]=-1;
    for (int i=1;i<=cnt;++i)
        lg2[i]=lg2[i >> 1]+1;
    for (int j=1;j<=20;++j)
        for (int i=1;i<=cnt-(1 << j)+1;++i)
            st[i][j]=(dep[st[i][j-1]]<=dep[st[i+(1 << j-1)][j-1]])?st[i][j-1]:st[i+(1 << j-1)][j-1];
    dfs2(1,1);
    for (int i=1;i<=n;++i)
        scanf("%d",&c[i]);
    crt=1,build(1,1,tt);
    for (int i=1;i<=n;++i)
    {
        
        int nc(lca(lc[c[i]],i));
        if (lc[c[i]])
            modify(c[i],lc[c[i]],nc);
        modify(c[i],i,nc);
        lc[c[i]]=nc;
    }
    for (int i=1;i<=crt;++i)
        if (!Dfn[i])
            tarjan(i);
    topo();
    printf("%d\n",ans-1);
    return 0;
}
posted @ 2021-04-13 19:01  GK0328  阅读(43)  评论(0编辑  收藏  举报