UOJ#61. 【UR #5】怎样更有力气
大力水手问禅师:“大师,很多事情都需要用很大力气才能完成,而我在吃了菠菜之后力气很大,于是就导致我现在非常依赖菠菜。我很讨厌我的现状,有没有办法少吃点菠菜甚至不吃菠菜却仍很有力气?”
禅师浅笑,答:“方法很简单,不过若想我教你,你需先下山徒手修路。”
山下是 n 座村庄从 1 到 n 编号,之间没有路相连。禅师给了大力水手一张草图,这张草图里 n 座村庄被 n−1 条双向道路连接,任意一个村庄都可以通过双向道路到达其它所有村庄。
现在大力水手要根据禅师的意思在村庄间修路。禅师规定大力水手需要在 m 天内完成任务,其中大力水手的修路方式如下:
- 第 i 天,禅师指定了两个村庄 vi 和 ui,在草图上 vi 号村庄到 ui 号村庄的最短路径上的所有村庄(包括 vi 和 ui)中,大力水手需要选出若干对村庄(一个村庄可以被重复选多次,当然大力水手在这天也可以一对村庄都不选),然后在选出的每一对村庄间修建双向道路。
- 在实地考察中大力水手发现,有 p 个限制关系 (ti,ai,bi),表示在第 ti 天无法在 ai 号村庄到 bi 号村庄间修路(路是双向的,所以自然也无法在 bi 号村庄到 ai 号村庄间修路)。
- 每一天都有个修理所需力气值 wi,表示在第 i 天每修建一条道路都要耗费 wi 点力气值。
大力水手开始蛮力干了起来,一罐又一罐地吞食菠菜,结果经常修建一些无用的道路,每天都累得筋疲力尽。
作为一个旁观者,请你帮大力水手求出要想让 m 天后任意一对村庄之间都可以互相到达,所需要的总力气值最少是多少。注意最后修出来的道路不必和草图一致。
输入格式
第一行三个非负整数 n,m,p。保证 n≥1。
接下来一行 n−1 个整数,其中第 i 个整数 fi+1 (1≤fi+1≤i)表示草图中 i+1 号村庄与 fi+1 号村庄间有一条双向道路。
接下来 m 行,第 i 行包含三个整数 vi,ui,wi (1≤vi,ui≤n,vi≠ui,1≤wi≤109)表示第 i 天禅师指定了 vi 号村庄和 ui 号村庄,大力水手修一条路耗费 wi 点力气值。
接下来 p 行,每行包含三个整数 ti,ai,bi 表示一个限制关系。保证 1≤ti≤m,1≤ai,bi≤n,ai≠bi,且草图上 ai 号村庄和 bi 号村庄都在 vti 号村庄到 uti 号村庄的最短路径上。另外,保证输入中不会出现重复的限制关系,即不会有两个限制关系 i,j 满足 ti=tj,ai=aj,bi=bj 或 ti=tj,ai=bj,bi=aj。
输出格式
输出一行一个整数,表示所需要的最小总力气值。保证至少存在一种修路的方法使得任意一对村庄之间都可以互相到达。
C/C++ 输入输出 long long 时请用 %lld
。C++ 可以直接使用 cin/cout 输入输出。
样例一
input
5 2 3
1 1 3 3
2 4 1
5 4 2
1 3 2
1 3 1
1 3 4
output
6
explanation
第一天大力水手本来可以在 (1,2),(1,3),(1,4),(2,3),(2,4),(3,4) 间修路,但是由于第一天不能在 (3,2),(3,1) 和 (3,4) 间修路,所以可能的选择只有 (1,2),(1,4),(2,4)。对于第二天,大力水手可能的选择有 (3,4),(3,5),(4,5)。
一种可能的最优方案是,第一天大力水手在 (1,2),(1,4) 间修路,第二天在 (3,4),(4,5) 间修路,总共耗费 1+1+2+2=6 点力气值。
样例二
见样例数据下载。这个样例中 fi+1=i。
样例三
见样例数据下载。
限制与约定
测试点编号 | n | m | p | 其他 |
---|---|---|---|---|
1 | n≤100 | m≤100 | p≤100 | 无 |
2 | n≤300000 | m≤300000 | p=0 | 无 |
3 | ||||
4 | ||||
5 | n≤300000 | m≤300000 | p≤300000 | 保证对于 1<i≤n, fi+1=i |
6 | ||||
7 | n≤300000 | m≤300000 | p≤300000 | 无 |
8 | ||||
9 | ||||
10 |
时间限制:1s
空间限制:256MB
后记
由于你的帮助,大力水手顺利修完了道路而且使用的力气值是原定计划的 0.01%。
大力水手对禅师说:“我明白了!我以前都是在使用蛮力,从今往后我要多思索,多使用巧力解决问题。”
禅师摆摆手,嘿嘿一笑:“对不起,我只是想请你帮忙修路而已。”
大力水手吃了一罐菠菜,把禅师打死了。
跪zsy,我终于知道怎样更有力气了.
考虑离线处理,先将所有天按w从小到大排序。设k为第i天的所有限制数,设u表示排序后第i天的u,v同理。
1、若dist(u,v)>k : 所有(u,v)的路径上的节点全部能连通。
2、若dist(u,v)<=k:这样总共的路径上点数=sigma(dist(u,v))<=p,我们只需想出一个只与路径上点数相关的算法。
对于1的情况,可以从下向上连边,用findset(tree,x)表示x向上第一个不与x连通的节点,findset(st,x)表示所有与x连通的节点,最多连n-1条边,故暴力连即可。
对于2的情况,我们把(u,v)的路径上的节点扔到一起并重新编号,考虑从每个点暴力向第一个不等于它的没有走过的点,判断是否有边(即是否有边的限制),用findset(path,x)表示大于x的第一个没有被访问过的点。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#define rep(s,t) for(int i=s;i<=t;i++)
#define ren for(int i=first[x];i!=-1;i=next[i])
using namespace std;
inline int read() {
int x=,f=;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-;
for(;isdigit(c);c=getchar()) x=x*+c-'';
return x*f;
}
const int maxn=;
int n,m,p,First[maxn],Next[maxn],to[maxn],e,dep[maxn],fa[maxn],anc[maxn][],cnt;
void AddEdge(int u,int v) {
to[++e]=v;Next[e]=First[u];First[u]=e;
}
int first[maxn],next[maxn],u1[maxn],v1[maxn];
void dfs(int x) {
anc[x][]=fa[x];dep[x]=dep[fa[x]]+;
rep(,) anc[x][i]=anc[anc[x][i-]][i-];
for(int i=First[x];i;i=Next[i]) dfs(to[i]);
}
int LCA(int x,int y) {
if(dep[x]<dep[y]) swap(x,y);
for(int i=;i>=;i--) if(dep[x]-dep[y]>=<<i) x=anc[x][i];
for(int i=;i>=;i--) if(anc[x][i]!=anc[y][i]) x=anc[x][i],y=anc[y][i];
return x==y?x:fa[x];
}
int lim[maxn],st[maxn],tree[maxn],path[maxn];
void Addop(int day,int u,int v) {
lim[day]++;
u1[++cnt]=u;v1[cnt]=v;next[cnt]=first[day];first[day]=cnt;
}
struct Pair {
int x,y;
bool operator < (const Pair& ths) const {
if(x!=ths.x) return x<ths.x;
return y<ths.y;
}
}B[maxn*];
int ToT,len;
struct Day {
int u,v,w,id;
bool operator < (const Day& ths) const {return w<ths.w;}
}A[maxn];
long long ans;
int findset(int* pa,int x) {return pa[x]<?x:pa[x]=findset(pa,pa[x]);}
int merge(int u,int v,int w) {
if((u=findset(st,u))!=(v=findset(st,v))) ans+=w,st[u]=v;
}
int list[maxn],id[maxn],T[maxn],L[maxn],R[maxn];
void dfs(int u,int v,int w) {
if(u!=v) merge(list[u],list[v],w);
path[findset(path,u)]=u+;
for(int i=findset(path,);i<=len;i=findset(path,i+)) {
int p=lower_bound(T+L[u],T+R[u]+,i)-T;
if(T[p]!=i) dfs(i,v,w);
}
}
int main() {
n=read();m=read();p=read();
rep(,n) AddEdge(fa[i]=read(),i);
dfs();rep(,n) st[i]=tree[i]=-;
rep(,m) A[A[i].id=i].u=read(),A[i].v=read(),A[i].w=read();
sort(A+,A+m+);
rep(,p) {
int day=read();
Addop(day,read(),read());
}
rep(,m) {
int u=A[i].u,v=A[i].v,w=A[i].w,day=A[i].id;
int lca=LCA(u,v);len=dep[u]+dep[v]-dep[lca]*;
if(len>lim[day]) {
while(dep[u=findset(tree,u)]>dep[lca]) merge(u,tree[u]=fa[u],w);
while(dep[v=findset(tree,v)]>dep[lca]) merge(v,tree[v]=fa[v],w);
}
else {
int tot=;ToT=;
while(u!=lca) id[list[tot]=u]=tot++,u=fa[u];
while(v!=lca) id[list[tot]=v]=tot++,v=fa[v];
id[list[tot]=lca]=tot++;
for(int i=first[day];i;i=next[i]) {
int u2=id[u1[i]],v2=id[v1[i]];
B[++ToT]=(Pair){u2,v2};B[++ToT]=(Pair){v2,u2};
}
for(int i=;i<=len+;i++) path[i]=-;
sort(B+,B+ToT+);T[]=B[].y;L[B[].x]=;
for(int i=;i<=ToT+;i++) {
T[i]=B[i].y;
if(B[i].x!=B[i-].x) {
R[B[i-].x]=i-;
if(i!=ToT+) L[B[i].x]=i;
}
}
for(int i=;i<=len;i++) if(findset(path,i)==i) dfs(i,i,w);
}
}
printf("%lld\n",ans);
return ;
}
UOJ#61. 【UR #5】怎样更有力气的更多相关文章
- 【UOJ#61】【UR #5】怎样更有力气(最小生成树)
[UOJ#61][UR #5]怎样更有力气(最小生成树) 题面 UOJ 题解 最最最暴力的想法是把所有边给处理出来然后跑\(MST\). 考虑边权的情况,显然离线考虑,把么一天按照\(w_i\)进行排 ...
- 「UR#5」怎样更有力气
「UR#5」怎样更有力气 解题思路 考虑没有限制的情况,一定是把操作离线下来,按照边权从小到达做.可以发现,如果没有限制,完全图是多余的,直接拿树边进行合并就可以了.我们要做这么一件事情,把每个点属于 ...
- YYHS-怎样更有力气
题目描述 OI大师抖儿在夺得银牌之后,顺利保送pku.这一天,抖儿问长者:"我虽然已经保送了,但我的志向是为国家健康工作五十年.请问我应该怎样变得更有力气?" 长者回答:&quo ...
- 【NOIP2017练习】怎样更有力气(二分答案,线性扫描)
题意:OI大师抖儿在夺得银牌之后,顺利保送pku.这一天,抖儿问长者:“我虽然已经保送了,但我的志向是为国家健康工作五十年.请问我应该怎样变得更有力气?” 长者回答:“你啊,Too Young T ...
- UOJ 【UR #5】怎样跑得更快
[UOJ#62]怎样跑得更快 题面 这个题让人有高斯消元的冲动,但肯定是不行的. 这个题算是莫比乌斯反演的一个非常巧妙的应用(不看题解不会做). 套路1: 因为\(b(i)\)能表达成一系列\(x(i ...
- 【UR #5】怎样更有力气
Problem Description 大力水手问禅师:"大师,很多事情都需要用很大力气才能完成,而我在吃了菠菜之后力气很大,于是就导致我现在非常依赖菠菜.我很讨厌我的现状,有没有办法少吃点 ...
- UOJ61. 【UR #5】怎样更有力气
题目链接 Statement 给定一棵 \(n\) 点树 \(T\) 和 \(m\) 个操作 v u w : 在 \(T\) 中 \(u,v\) 的最短路上所有点里面选出若干对(可以不选,可以重复), ...
- UOJ #22 UR #1 外星人
LINK:#22. UR #1 外星人 给出n个正整数数 一个初值x x要逐个对这些数字取模 问怎样排列使得最终结果最大 使结果最大的方案数又多少种? n<=1000,x<=5000. 考 ...
- UOJ.52.[UR #4]元旦激光炮(交互 思路)
题目链接 \(Description\) 交互库中有三个排好序的,长度分别为\(n_a,n_b,n_c\)的数组\(a,b,c\).你需要求出所有元素中第\(k\)小的数.你可以调用至多\(100\) ...
随机推荐
- BZOJ2904
找了一个晚上的资料,拼凑出来这么一个东西: 1) 如果是完全平方数返回12) 如果可以表示成形如$x^2+y^2$的形式输出2.这要求该数质因数分解后形如$4k+3$的质因数次数都是偶数.3) 如果该 ...
- CSS3弹性盒模型flexbox完整版教程
http://caibaojian.com/flexbox-guide.html 来自CSS Tricks上的一个教程,原文为:A Complete Guide to Flexbox.文中详细的介绍了 ...
- 【OpenStack】OpenStack系列17之OpenStack私有云设计一
[软件系统] 1.操作系统(Minimal最小化安装): CentOS-6.6-x86_64,CentOS 6最后一个版本,官方建议版本. 相对于6.5版本: 强化对 SCSI 设备的处理,有助应付某 ...
- css行内样式
<title>归园田居</title> </head> <body> <h2>归园田居</h2> <p>种豆南山下, ...
- Live Archive 3644 X-Plosives 解题报告
题目链接:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&category=1 ...
- js监听密码输入框type
1.密码输入框 <input class="oaInput oaText" type="text" placeholder="请输入用户名&qu ...
- 【python】any()和all()
any(iterable) 版本:该函数适用于2.5以上版本,兼容python3版本. 说明:如果iterable的任一元素不为0.''.False,返回True. all(iterable) 说明: ...
- 当Android工程中提示你找不到头文件,但你已经设置头文件路径了
虽然在Android.mk文件中,配置了LOCAL_C_INCLUDES路径,但是工程中的红色叉号一直提示找不到头文件 这时,你在工程树目录中展开Includes项,捣鼓捣鼓,重新build下,或许就 ...
- 谈谈异步加载JavaScript
前言 关于JavaScript脚本加载的问题,相信大家碰到很多.主要在几个点—— 1> 同步脚本和异步脚本带来的文件加载.文件依赖及执行顺序问题 2> 同步脚本和异步脚本带来的性能优化问题 ...
- Android之多线程断点下载
本文主要包含多线程下载的一些简单demo,包括三部分 java实现 android实现 XUtils开源库实现 注意下载添加网络权限与SD卡读写权限 java实现多线程下载 public class ...