题目

  点这里看题目。

分析

  做法比较容易看出来。我们对于每个城市,找出那些 " 如果这个城市在首都内,则必须在首都内的其它城市 " ,也就是为了让这个城市的小镇连通而必须选的城市。

  接着,我们新建一个有向图,将一个城市看成一个点,一条边\((u,v)\)代表 " \(u\)在首都则\(v\)必在首都 " ,即上文所说的关系。对这个图进行强连通分量分解。假想我们对这个图缩点。缩点后的图上,一个点如果被选在首都内,它所能到达的点都必须选在首都内,我们称选定它的代价为它所能到达的城市的个数(注意这里说的是城市,即未缩点的图上的点)。因此最优策略一定会选定一个没有出度的点,答案即是没有出度的点中的最小代价。

  强连通分量分解可以用 Tarjan ,时间是线性的;而前面的有向图可能会被卡到\(O(n^2)\),因此我们考虑优化建这个有向图。

  瓶颈在于如何确定哪些城市是必须选的。对于树上多个颜色的情况,我们一般会想到用虚树。因此,我们就对于每一个城市,建起它的虚树。可以发现,虚树上的小镇一定会被选定,它们所属的城市一定会被选定。因此,对于每一个小镇,它到它所在城市的虚树的根上的所有小镇都是必选的。这是一个树上路径覆盖情况,我们可以用倍增优化建图。

  具体而言,我们对每个城市建立一个点;让每个小镇向它所在的城市连一条边;构造倍增辅助图;对于每个小镇,让它所在的城市连到上文所说的路径对应的辅助点上(类似 RMQ 的 ST 表,这里连接的辅助点所对应的小镇可以重复,但不能遗漏),可以发现每次最多连两个辅助点。

  这样的图的点是\(O(n\log_2n)\),边是\(O(n\log_2n)\)。然后跑 Tarjan 计算答案即可。

代码

#include <cmath>
#include <cstdio>
#include <vector>
using namespace std; const int MAXN = 2e5 + 5, MAXLOG = 20, MAXS = MAXN * MAXLOG; template<typename _T>
void read( _T &x )
{
x = 0;char s = getchar();int f = 1;
while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
while( s >= '0' && s <= '9' ){x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar();}
x *= f;
} template<typename _T>
void write( _T x )
{
if( x < 0 ){ putchar( '-' ); x = ( ~ x ) + 1; }
if( 9 < x ){ write( x / 10 ); }
putchar( x % 10 + '0' );
} template<typename _T>
_T MIN( const _T a, const _T b )
{
return a < b ? a : b;
} struct edge
{
int to, nxt;
}Graph[MAXS << 1]; vector<int> G[MAXN], town[MAXN], SCC[MAXS]; int siz[MAXS], deg[MAXS];
int head[MAXS], DFN[MAXS], LOW[MAXS], bel[MAXS], sta[MAXS];
int f[MAXN][MAXLOG], id[MAXN][MAXLOG];
int c[MAXN], dep[MAXN], pos[MAXN];
int N, K, lg2, ID, tot, top, cnt;
bool inSta[MAXS]; void addEdge( const int from, const int to )
{
Graph[++ cnt].to = to, Graph[cnt].nxt = head[from];
head[from] = cnt;
} void DFS( const int u, const int fa )
{
f[u][0] = fa;
if( f[u][0] ) id[u][0] = ++ tot, addEdge( tot, fa ), addEdge( tot, u );
dep[u] = dep[fa] + 1, pos[u] = ++ ID;
for( int i = 0, v ; i < G[u].size() ; i ++ )
if( ( v = G[u][i] ) ^ fa )
DFS( v, u );
} void init()
{
lg2 = log2( N );
for( int j = 1 ; j <= lg2 ; j ++ )
for( int i = 1 ; i <= N ; i ++ )
{
f[i][j] = f[f[i][j - 1]][j - 1];
if( ! f[i][j] ) continue; id[i][j] = ++ tot;
addEdge( id[i][j], id[i][j - 1] ), addEdge( id[i][j], id[f[i][j - 1]][j - 1] );
}
} void balance( int &u, const int s ) { for( int i = 0 ; ( 1 << i ) <= s ; i ++ ) if( s & ( 1 << i ) ) u = f[u][i]; } void cover( int u, int s )
{
if( ! s ) return ;
int k = log2( s ), col = c[u] + N;
addEdge( col, id[u][k] );
if( ! ( s -= 1 << k ) ) return ;
balance( u, s ), addEdge( col, id[u][k] );
} int LCA( int u, int v )
{
if( dep[u] > dep[v] ) balance( u, dep[u] - dep[v] );
if( dep[v] > dep[u] ) balance( v, dep[v] - dep[u] );
if( u == v ) return u;
for( int i = lg2 ; ~ i ; i -- ) if( f[u][i] ^ f[v][i] ) u = f[u][i], v = f[v][i];
return f[u][0];
} void Tarjan( const int u )
{
DFN[u] = LOW[u] = ++ ID;
inSta[sta[++ top] = u] = true;
for( int i = head[u], v ; i ; i = Graph[i].nxt )
{
if( ! DFN[v = Graph[i].to] ) Tarjan( v ), LOW[u] = MIN( LOW[u], LOW[v] );
else if( inSta[v] ) LOW[u] = MIN( LOW[u], DFN[v] );
}
if( LOW[u] == DFN[u] )
{
int v; tot ++;
do inSta[v = sta[top --]] = false, SCC[bel[v] = tot].push_back( v ); while( v ^ u );
}
} int main()
{
read( N ), read( K ); tot = N + K;
for( int i = 1, a, b ; i < N ; i ++ ) read( a ), read( b ), G[a].push_back( b ), G[b].push_back( a );
for( int i = 1 ; i <= N ; i ++ ) read( c[i] ), town[c[i]].push_back( i ), addEdge( i, c[i] + N );
DFS( 1, 0 );
init();
for( int i = 1 ; i <= K ; i ++ )
{
int mn = town[i][0], mx = town[i][0];
for( int u, j = 1 ; j < town[i].size() ; j ++ )
{
u = town[i][j];
if( pos[u] < pos[mn] ) mn = u;
if( pos[u] > pos[mx] ) mx = u;
}
int lca = LCA( mx, mn );
for( int j = 0 ; j < town[i].size() ; j ++ )
cover( town[i][j], dep[town[i][j]] - dep[lca] );
}
int up = tot; tot = ID = 0;
for( int i = 1 ; i <= up ; i ++ )
if( ! DFN[i] )
Tarjan( i );
for( int i = 1 ; i <= tot ; i ++ )
for( int j = 0 ; j < SCC[i].size() ; j ++ )
if( N < SCC[i][j] && SCC[i][j] <= N + K )
siz[i] ++;
int ans = N;
for( int i = 1 ; i <= tot ; i ++ )
for( int j = 0 ; j < SCC[i].size() ; j ++ )
for( int k = head[SCC[i][j]] ; k ; k = Graph[k].nxt )
if( bel[Graph[k].to] ^ i )
deg[i] ++;
for( int i = 1 ; i <= tot ; i ++ ) if( ! deg[i] ) ans = MIN( ans, siz[i] - 1 );
write( ans ), putchar( '\n' );
return 0;
}

「JOISC 2020 Day4」首都城市的更多相关文章

  1. 【LOJ】#3036. 「JOISC 2019 Day3」指定城市

    LOJ#3036. 「JOISC 2019 Day3」指定城市 一个点的可以dp出来 两个点也可以dp出来 后面的就是在两个点的情况下选一条最长的链加进去,用线段树维护即可 #include < ...

  2. LOJ#2882. 「JOISC 2014 Day4」两个人的星座(计算几何)

    题面 传送门 题解 我们发现如果两个三角形相离,那么这两个三角形一定存在两条公切线 那么我们可以\(O(n^2)\)枚举其中一条公切线,然后可以暴力\(O(n^3)\)计算 怎么优化呢?我们可以枚举一 ...

  3. @loj - 3039@ 「JOISC 2019 Day4」蛋糕拼接 3

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 今天是 IOI 酱的生日,所以她的哥哥 JOI 君给她预定了一个 ...

  4. 「JOISC 2020 Day2」变态龙之色 题解

    题目传送门 注意 同性必定不同色 必有一个同色异性,且不相互不喜欢 Solution 我们发现,我们问题比较大的就是如何确定性别问题.我们可以一个一个加进去,在原来已经确定了的二分图上增加新的性别关系 ...

  5. 「JOISC 2014 Day4」两个人的星座

    首先突破口肯定在三角形不交,考虑寻找一些性质. 引理一:两个三角形不交当且仅当存在一个三角形的一条边所在直线将两个三角形分为异侧 证明可以参考:三角形相离充要条件,大致思路是取两个三角形重心连线,将其 ...

  6. 「JOISC 2019 Day4」蛋糕拼接 3

    loj 3039 NKOJ Description \(n\)个蛋糕,每个蛋糕有\(w_i,h_i\).选\(m\)个蛋糕满足\(\sum\limits_{j=1}^mw_{k_j}-\sum\lim ...

  7. 「JOISC 2020 Day1」汉堡肉

    我终于学会打开机房的LOJ了! description LOJ3272 有\(n(n<=2*10^5)\)个矩形,让你找\(k(k<=4)\)个点可以覆盖所有矩形(点可重复),输出一种方案 ...

  8. 「JOISC 2019 Day3」穿越时空 Bitaro

    「JOISC 2019 Day3」穿越时空 Bitaro 题解: ​ 不会处理时间流逝,我去看了一眼题解的图,最重要的转换就是把(X,Y)改成(X,Y-X)这样就不会斜着走了. ​ 问题变成二维平面上 ...

  9. 【LOJ】#3034. 「JOISC 2019 Day2」两道料理

    LOJ#3034. 「JOISC 2019 Day2」两道料理 找出最大的\(y_{i}\)使得\(sumA_{i} + sumB_{y_i} \leq S_{i}\) 和最大的\(x_{j}\)使得 ...

随机推荐

  1. UVALive3720

    题目大意:见刘汝佳<算法竞赛入门经典——训练指南>P173. 解题思路: 问题可以转化为求共有多少条过点阵中的点的斜线.其中必定包含左斜线和右斜线,由于点阵式对称的,所以我们只需求出左右斜 ...

  2. Understanding REST and RESTful APIs

    Understanding REST and RESTful APIs If you've spent any amount of time with modern web development, ...

  3. Kubernetes学习笔记(七):访问Pod元数据与Kubernetes API

    Downward API 我们已经了解到,使用ConfigMap和Secret向应用传递配置数据,这对于运行前预设的数据是可行的.但是对于那些不能预先知道的,就需要使用Downward API. Do ...

  4. 获取MP4媒体文件时长

    由于之前上传MP4文件没有保存视频时长,现在有需要,所以只好写代码读取时长.找了几个发现是 c/c++ 实现,或者是借助 FFmpeg 实现. 一个偶然在 GitHub 上面发现一个 c 文件,由于获 ...

  5. 使用websocket开发智能聊天机器人

    前面我们学习了异步web框架(sanic)和http异步调用库httpx,今天我们学习websocket技术. websocket简介 我们知道HTTP协议是:请求->响应,如果没有响应就一直等 ...

  6. ansible模块详解

    [简单介绍] Ansible是一种agentless(基于ssh),可实现批量配置.命令执行和控制,基于Python实现的自动化运维工具. 有以下两个特性: 模块化:通过调用相关模块,完成指定任务,且 ...

  7. Postman+Newman+Git+Jenkins接口自动化测试

    一.Postman  1.创建Collection,在Collection中创建接口请求,如下图所示. 2.编写接口对应的断言Test和Pre-request Script,如下图所示. 3.配置接口 ...

  8. vue 使用 elementUI 和 antd 的细微差别

    Checkbox a-checkbox 没有value属性,绑定用checked Collapse 使用折叠面板的时候,antd 的层级关系是 .ant-collapse >.ant-colla ...

  9. 【Hadoop】配置全分布式模式

    分布式原理 配置 详细过程 假设有三台虚拟机,1台master主机namenode,2台slave奴隶机datanode 所有机器都要配好jdk.Java环境变量.hadoop_env.sh里java ...

  10. MyBatis(三)动态SQL与缓存

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) 一.动态SQL语句 准备工作: public class User { private int id; ...