「JOISC 2020 Day4」首都城市
题目
点这里看题目。
分析
做法比较容易看出来。我们对于每个城市,找出那些 " 如果这个城市在首都内,则必须在首都内的其它城市 " ,也就是为了让这个城市的小镇连通而必须选的城市。
接着,我们新建一个有向图,将一个城市看成一个点,一条边\((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」首都城市的更多相关文章
- 【LOJ】#3036. 「JOISC 2019 Day3」指定城市
LOJ#3036. 「JOISC 2019 Day3」指定城市 一个点的可以dp出来 两个点也可以dp出来 后面的就是在两个点的情况下选一条最长的链加进去,用线段树维护即可 #include < ...
- LOJ#2882. 「JOISC 2014 Day4」两个人的星座(计算几何)
题面 传送门 题解 我们发现如果两个三角形相离,那么这两个三角形一定存在两条公切线 那么我们可以\(O(n^2)\)枚举其中一条公切线,然后可以暴力\(O(n^3)\)计算 怎么优化呢?我们可以枚举一 ...
- @loj - 3039@ 「JOISC 2019 Day4」蛋糕拼接 3
目录 @description@ @solution@ @accepted code@ @details@ @description@ 今天是 IOI 酱的生日,所以她的哥哥 JOI 君给她预定了一个 ...
- 「JOISC 2020 Day2」变态龙之色 题解
题目传送门 注意 同性必定不同色 必有一个同色异性,且不相互不喜欢 Solution 我们发现,我们问题比较大的就是如何确定性别问题.我们可以一个一个加进去,在原来已经确定了的二分图上增加新的性别关系 ...
- 「JOISC 2014 Day4」两个人的星座
首先突破口肯定在三角形不交,考虑寻找一些性质. 引理一:两个三角形不交当且仅当存在一个三角形的一条边所在直线将两个三角形分为异侧 证明可以参考:三角形相离充要条件,大致思路是取两个三角形重心连线,将其 ...
- 「JOISC 2019 Day4」蛋糕拼接 3
loj 3039 NKOJ Description \(n\)个蛋糕,每个蛋糕有\(w_i,h_i\).选\(m\)个蛋糕满足\(\sum\limits_{j=1}^mw_{k_j}-\sum\lim ...
- 「JOISC 2020 Day1」汉堡肉
我终于学会打开机房的LOJ了! description LOJ3272 有\(n(n<=2*10^5)\)个矩形,让你找\(k(k<=4)\)个点可以覆盖所有矩形(点可重复),输出一种方案 ...
- 「JOISC 2019 Day3」穿越时空 Bitaro
「JOISC 2019 Day3」穿越时空 Bitaro 题解: 不会处理时间流逝,我去看了一眼题解的图,最重要的转换就是把(X,Y)改成(X,Y-X)这样就不会斜着走了. 问题变成二维平面上 ...
- 【LOJ】#3034. 「JOISC 2019 Day2」两道料理
LOJ#3034. 「JOISC 2019 Day2」两道料理 找出最大的\(y_{i}\)使得\(sumA_{i} + sumB_{y_i} \leq S_{i}\) 和最大的\(x_{j}\)使得 ...
随机推荐
- JS 如何获取自定义属性
<script>var testEle = document.getElementById("test"); testEle.setAttribute("de ...
- Excel导出到DataSet
#region 导入excel 返回Dataset public DataSet ExecleDataSet(string filename, string file, string Type) { ...
- SSL F5
应用交付领域经常提到SSL加速,但SSL加速到底是什么意思?SSL加速和F5指什么意思呢?在网上查询和整理了一些关于SSL加速和F5的相关解释,仅供参考:SSL加速:加密套接层协议(简称SSL)是网络 ...
- Write a merge sort program
Merge Sort- Recursion Write a merge sort program in JavaScript. Sample array : [34, 7, 23, 32, 5, 62 ...
- JAVA自定义数据类型用法
一,自定义数据类型的概念: 我们就拿一部手机进行分析,它能用来做什么呢?它可以打电话,上网,聊微信等,这些就是 手机所提供的功能,也就是方法:手机也有它的特征,如颜色.尺寸大小.品牌型号等,这些 ...
- js数组对象的一些常用方法
pop:删除数组最后一个元素 语法: array.pop(); 如 var array = ['1','2','3']; array.pop(); 返回结果:[‘1’,‘2’]此方法会改变数组的长度 ...
- 5.Linux的启动过程和系统指令
1.Linux的启动过程 作为一台计算机,启动它的第一步是加电自检,也就是给电脑用电然后按电源按钮开机.加电之后的运行步骤:(1)加载bios,然后检查硬盘信息 (2)读取MBR的配置(MBR就是硬盘 ...
- S32DS编译程序出现Type region `SRAM' overflowed by 19240 bytes错误
用S32DS编译工程遇到Type region `SRAM' overflowed by 19240 bytes错误(芯片为S9KEAZ64AMLH) 程序中未初始化的变量存放SRAM中 当程序出现未 ...
- Python 每日一练 | Flask 实现半成品留言板
留言板Flask实现 引言 看了几天网上的代码,终于写出来一个半成品的Flask的留言板项目,为什么说是半成品呢?因为没能实现留言板那种及时评论刷新的效果,可能还是在重定向上有问题 或者渲染写的存在问 ...
- Java中的集合(六)继承Collection的Set接口
Java中的集合(六)继承Collection的Set接口 一.Set接口的简介 Set接口和List接口都是继承自Collection接口,它与Collection接口中功能基本一致,并没有对Col ...