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;
}