UOJ61. 【UR #5】怎样更有力气
Statement
给定一棵 \(n\) 点树 \(T\) 和 \(m\) 个操作 v u w :
- 在 \(T\) 中 \(u,v\) 的最短路上所有点里面选出若干对(可以不选,可以重复),在每一对之间加边,花费为 \(w\) .
- 有 \(p\) 个限制
t a b,表示第 \(t\) 次不能在 \(a,b\) 之间加边,保证 \(a,b\) 在 \(u,v\) 的路径上且无重复。
求所有加的边形成 \(n\) 点连通图的最小代价。 \(n,m,p\leq 3e5\) .
Solution
一开始看错题了
一个显然的想法是,把每一天按照 \(w\) 升序,然后每次尽可能多地合并连通块。
有一个显然的性质: 对于第 \(i\) 天,设当天有 \(num\) 个限制,如果 \(dis(u,v)>num\) ,那么这一条路径上的所有点之间都能连通,且必须连通
Proof
长度是 \(dis(u,v)\) ,点数就是 \(dis(u,v)+1\) .既然 \(dis(u,v)\) 比 \(num\) 大,那么就算一个点包揽了 \(num\) 个限制,也至少能向一个点连边,这样就一定可以使得路径上所有点连通。而由于我们是将每一次操作按 \(w\) 升序的,所以这一次将它连边一定不会比后面再做劣,因此这种情况下,第 \(i\) 次做完之后一定是 \(path(u,v)\) 全部连通。
那么对于满足这个性质的情况,直接连就好了,反正连完就是同一个连通块。(网上题解为啥说的是能直接连边啊……虽然是等价的)
现在来考虑 \(dis(u,v)\leq num\) 的情况。
首先,对于 \(t\) 的所有限制,建立一个子图 \(g\) ,并暴力把 \(u\to v\) 路径上的所有点都存下来,设为 \(path[]\) .令 \(t\) 的限制个数为 \(k\) .
然后,选取一个度数最小的点 \(x\) ,将路径上的点集分成两类:\(V_1\) 中的点在限制子图中和 \(x\) 相邻,\(V_2\) 反之。
显然,对于 \(V_2\) 的点,直接和 \(x\) 连边就好了。
对于 \(V_1\) 中的点 \(y\) ,如果 \(deg[y]<|V_2|\) (这里的度数是和 \(V_2\) 的度数),那么至少能和 \(V_2\) 中一个点连边,直接加边即可。复杂度 \(\mathcal{O}(\sum_y deg[y])=\mathcal{O}(k)\) .
否则,暴力枚举 \(V_1\) 中的点,尝试两两连边。由于度数最小所以 \(deg[x]\) 是根号级别的,复杂度 \(\mathcal{O}(k)\) .
所以总体复杂度是 \(\mathcal{O}(n\log n)\) (同级复杂度),瓶颈在于排序。
Code
//Author: RingweEH
const int N=3e5+10;
struct Date
{
int u,v,w,t;
bool operator < ( const Date &tmp ) const { return w<tmp.w; }
}a[N];
struct DSU
{
int fa[N];
int find( int x ) { return x==fa[x] ? x : fa[x]=find(fa[x]); }
DSU() { for ( int i=1; i<=N-10; i++ ) fa[i]=i; }
}D1,D2;
int n,m,k,fa[N],dep[N];
ll ans=0;
bool vis[N];
vector<Date> restr[N];
vector<int> g[N];
stack<Date> sta;
bool Check_Length( int u,int v,int num ) //判断dis(u,v)的长度和限制个数的大小关系
{
while ( num-- )
{
if ( dep[u]>dep[v] ) swap( u,v );
v=fa[v];
if ( u==v ) return 0;
}
return 1;
}
void Merge( int u,int v,int w )
{
u=D2.find( u ); v=D2.find( v );
if ( u!=v ) D2.fa[v]=u,ans+=w;
}
void Add( int u,int v )
{
g[u].push_back( v ); g[v].push_back( u );
Date tmp; tmp.u=u; tmp.v=v; tmp.w=tmp.t=0; sta.push( tmp );
}
void Clear( int u=0,int v=0 )
{
while ( !sta.empty() )
{
u=sta.top().u; v=sta.top().v; sta.pop();
g[u].clear(); g[v].clear();
}
}
int path[N],lenth;
void Get_Path( int u,int v )
{
lenth=0; bool fl=0;
do
{
fl=(u==v);
if ( dep[u]>dep[v] ) swap( u,v );
path[++lenth]=v; v=fa[v];
} while ( !fl );
}
int main()
{
n=read(); m=read(); k=read();
for ( int i=2; i<=n; i++ )
fa[i]=read(),dep[i]=dep[fa[i]]+1;
for ( int i=1; i<=m; i++ )
a[i].u=read(),a[i].v=read(),a[i].w=read(),a[i].t=i;
for ( int i=1,u,v,t; i<=k; i++ )
t=read(),u=read(),v=read(),restr[t].push_back( (Date){u,v,0,0} );
sort( a+1,a+1+m );
for ( int i=1; i<=m; i++ )
{
int u=a[i].u,v=a[i].v,w=a[i].w,t=a[i].t;
if ( Check_Length(u,v,restr[t].size()) ) //满足性质
{
u=D1.find( u ); v=D1.find( v );
while ( u^v )
{
if ( dep[u]>dep[v] ) swap( u,v );
Merge( v,fa[v],w ); D1.fa[v]=fa[v]; v=D1.find( v );
}
}
else
{
for ( Date j : restr[t] )
Add( j.u,j.v ); //对于限制建子图
Get_Path( u,v ); int mn=path[1];
for ( int j=2; j<=lenth; j++ ) //找度数最小的点
if ( g[path[j]].size()<g[mn].size() ) mn=path[j];
for ( int j : g[mn] ) //标记相邻点
vis[j]=1;
for ( int j=1; j<=lenth; j++ ) //x & V2
if ( !vis[path[j]] ) Merge( mn,path[j],w );
for ( int j : g[mn] )
{
int sum=0;
for ( int p : g[j] )
sum+=vis[p];
if ( g[j].size()-sum-1<lenth-g[mn].size()-1 ) Merge( j,mn,w );
//deg[y]<|V2|
}
for ( int j : g[mn] )
vis[j]=0;
for ( int j : g[mn] )
{
for ( int p : g[j] )
vis[p]=1;
for ( int p : g[mn] ) //V1中两两尝试连边
if ( !vis[p] ) Merge( j,p,w );
for ( int p : g[j] )
vis[p]=0;
}
Clear();
}
}
printf( "%lld\n",ans );
return 0;
}
UOJ61. 【UR #5】怎样更有力气的更多相关文章
- 【UOJ#61】【UR #5】怎样更有力气(最小生成树)
[UOJ#61][UR #5]怎样更有力气(最小生成树) 题面 UOJ 题解 最最最暴力的想法是把所有边给处理出来然后跑\(MST\). 考虑边权的情况,显然离线考虑,把么一天按照\(w_i\)进行排 ...
- 「UR#5」怎样更有力气
「UR#5」怎样更有力气 解题思路 考虑没有限制的情况,一定是把操作离线下来,按照边权从小到达做.可以发现,如果没有限制,完全图是多余的,直接拿树边进行合并就可以了.我们要做这么一件事情,把每个点属于 ...
- UOJ#61. 【UR #5】怎样更有力气
大力水手问禅师:“大师,很多事情都需要用很大力气才能完成,而我在吃了菠菜之后力气很大,于是就导致我现在非常依赖菠菜.我很讨厌我的现状,有没有办法少吃点菠菜甚至不吃菠菜却仍很有力气?” 禅师浅笑,答:“ ...
- YYHS-怎样更有力气
题目描述 OI大师抖儿在夺得银牌之后,顺利保送pku.这一天,抖儿问长者:"我虽然已经保送了,但我的志向是为国家健康工作五十年.请问我应该怎样变得更有力气?" 长者回答:&quo ...
- 【NOIP2017练习】怎样更有力气(二分答案,线性扫描)
题意:OI大师抖儿在夺得银牌之后,顺利保送pku.这一天,抖儿问长者:“我虽然已经保送了,但我的志向是为国家健康工作五十年.请问我应该怎样变得更有力气?” 长者回答:“你啊,Too Young T ...
- 【UR #5】怎样更有力气
Problem Description 大力水手问禅师:"大师,很多事情都需要用很大力气才能完成,而我在吃了菠菜之后力气很大,于是就导致我现在非常依赖菠菜.我很讨厌我的现状,有没有办法少吃点 ...
- [UOJ61]怎样更有力气
这个题还是挺有意思的... 一个小结论是:在一个$n$点$m$边的图中,如果度数最小的点度数为$d$,那么$d^2=O(m)$,因为$d\leq\frac{2m}n$,所以$d^2\leq dn\le ...
- 校际联合Contest
每次开一个坑都像是重新被碾压的预感 最近的新闻,以前很喜欢乔任梁的<复活>...然后他就死了...感觉我再多愁善感一点的话...就要悲伤逆流成河了吧... Contest 09/24(乐滋 ...
- 如何编写高质量的 jQuery 代码?
想必大家对于jQuery这个最流行的javascript类库都不陌生,而且只要是前端开发人员肯定或多或少的使用或者接触过,在今天的这篇文章中,我们将介绍一些书写高质量jQuery代码的原则,我们不单单 ...
随机推荐
- TextView之富文本
项目中使用富文本比较常见了,一行显示多种样式颜色的文本,使用 ClickableSpan 富文本实现在同一个 TextView 中的文本的颜色.大小.背景色等属性的多样化和个性化. 我们也可以使用Ht ...
- FreeMark导出word文件
1.编辑好word 2.将word模板另存为xml格式, 把需要动态生成的文字用${xxx}代替 eg: 张强 替换为:${name} 注意:图片是很长的一个base64的字符,同样替换就好 比如替换 ...
- To the Max(动态规划)
Description Given a two-dimensional array of positive and negative integers, a sub-rectangle is any ...
- 深度分析:Java并发编程之线程池技术,看完面试这个再也不慌了!
线程池的好处 Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池.在开发过程中,合理地使用线程池,相对于单线程串行处理(Serial Processing ...
- 使用CleanMyMac快速管理应用程序 优化Mac
CleanMyMac作为一款专业的苹果电脑清理软件,它不仅仅能单纯的卸载不用.少用的应用,同时还支持:1.清理应用程序的数据文件,将应用重置回初始状态,减少空间占用:2.自动检查应用更新,保持应用的最 ...
- guitar pro系列教程(十九):Guitar Pro添加音符之前我们要做什么?
前面的章节我们已经讲了不少关于{cms_selflink page='index' text='Guitar Pro'}的功能之类的讲解,那一般我们在打谱之前要做的是什么呢,很多新手玩家,对这方面也是 ...
- FLstudio 系列教程(七):FL Studio五大常用按钮介绍
我们打开FL Studio编曲软件会发现界面中有好多的菜单和窗口,这些窗口每个都有其单独的功能.今天小编主要给大家详细讲解下FL Studio水果软件的五大常用按钮. 1.首先我,我们双击桌面的水果图 ...
- sharding调试setAutoCommit
emmm应该是有manager的 因为指定了@Primary 不然容器启动的时候创建sessionFactory就因为多个DataSource异常了,后面会滚的时候manager为null也会有运行时 ...
- 【mq读书笔记】mq读写分离机制
mq根据brokerName查找Broker地址的过程 mq根据MessageQueue查找Broker地址的唯一依据是brokerName,同一组Broker(M-S)他们的bokerName相同但 ...
- redis面试问题(一)
五大常用数据类型 redis与其他缓存的比较 rdb和aof 主从复制,读写分离,哨兵机制 -------------------------------- 1.为什么使用redis (一)性能 我们 ...