UOJ#61. 【UR #5】怎样更有力气

大力水手问禅师:“大师,很多事情都需要用很大力气才能完成,而我在吃了菠菜之后力气很大,于是就导致我现在非常依赖菠菜。我很讨厌我的现状,有没有办法少吃点菠菜甚至不吃菠菜却仍很有力气?”

禅师浅笑,答:“方法很简单,不过若想我教你,你需先下山徒手修路。”

山下是 n 座村庄从 1 到 n 编号,之间没有路相连。禅师给了大力水手一张草图,这张草图里 n 座村庄被 n−1 条双向道路连接,任意一个村庄都可以通过双向道路到达其它所有村庄。

现在大力水手要根据禅师的意思在村庄间修路。禅师规定大力水手需要在 m 天内完成任务,其中大力水手的修路方式如下:

  1. 第 i 天,禅师指定了两个村庄 vi 和 ui,在草图上 vi 号村庄到 ui 号村庄的最短路径上的所有村庄(包括 vi 和 ui)中,大力水手需要选出若干对村庄(一个村庄可以被重复选多次,当然大力水手在这天也可以一对村庄都不选),然后在选出的每一对村庄间修建双向道路。
  2. 在实地考察中大力水手发现,有 p 个限制关系 (ti,ai,bi),表示在第 ti 天无法在 ai 号村庄到 bi 号村庄间修路(路是双向的,所以自然也无法在 bi 号村庄到 ai 号村庄间修路)。
  3. 每一天都有个修理所需力气值 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=0,f=1;char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c==‘-‘) f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-‘0‘;
    return x*f;
}
const int maxn=300010;
int n,m,p,First[maxn],Next[maxn],to[maxn],e,dep[maxn],fa[maxn],anc[maxn][20],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][0]=fa[x];dep[x]=dep[fa[x]]+1;
    rep(1,19) anc[x][i]=anc[anc[x][i-1]][i-1];
    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=19;i>=0;i--) if(dep[x]-dep[y]>=1<<i) x=anc[x][i];
    for(int i=19;i>=0;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*2];
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]<0?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+1;
    for(int i=findset(path,0);i<=len;i=findset(path,i+1)) {
        int p=lower_bound(T+L[u],T+R[u]+1,i)-T;
        if(T[p]!=i) dfs(i,v,w);
    }
}
int main() {
    n=read();m=read();p=read();
    rep(2,n) AddEdge(fa[i]=read(),i);
    dfs(1);rep(1,n) st[i]=tree[i]=-1;
    rep(1,m) A[A[i].id=i].u=read(),A[i].v=read(),A[i].w=read();
    sort(A+1,A+m+1);
    rep(1,p) {
        int day=read();
        Addop(day,read(),read());
    }
    rep(1,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]*2;
        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=0;ToT=0;
            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=0;i<=len+1;i++) path[i]=-1;
            sort(B+1,B+ToT+1);T[1]=B[1].y;L[B[1].x]=1;
            for(int i=2;i<=ToT+1;i++) {
                T[i]=B[i].y;
                if(B[i].x!=B[i-1].x) {
                    R[B[i-1].x]=i-1;
                    if(i!=ToT+1) L[B[i].x]=i;
                }
            }
            for(int i=0;i<=len;i++) if(findset(path,i)==i) dfs(i,i,w);
        }
    }
    printf("%lld\n",ans);
    return 0;
}

时间: 2024-10-12 17:06:49

UOJ#61. 【UR #5】怎样更有力气的相关文章

「UR#5」怎样更有力气

「UR#5」怎样更有力气 解题思路 考虑没有限制的情况,一定是把操作离线下来,按照边权从小到达做.可以发现,如果没有限制,完全图是多余的,直接拿树边进行合并就可以了.我们要做这么一件事情,把每个点属于的图上联通块看做颜色,每次合并链上相邻两块颜色不一样的,那么我们再额外使用一个并查集,把树上相邻的颜色相同的点合并在一个集合里,每次跳到集合中最浅的点做图上的合并操作即可,复杂度 \(\mathcal O(n\alpha(n))\) . 考虑一个操作的限制数量 \(cnt\) ,如果 \(cnt \

UOJ #62 怎样跑得更快

UOJ #62 怎样跑得更快 题目传送门 题意 大力水手问禅师:"大师,我觉得我光有力气是不够的.比如我吃菠菜可以让力气更大,但是却没有提升跑步的速度.请问怎样才能跑得更快?我试过吃白菜,没有效果." 禅师浅笑,答:"方法很简单,不过若想我教你,你先看看这道\(UOJ\) \(Round\)的\(C\)题." 令 \(p=998244353\)(\(7×17×223+17×17×223+1\),一个质数. 给你整数 \(n,c,d\).现在有整数\(x_1,-,x_

YYHS-怎样更有力气

题目描述 OI大师抖儿在夺得银牌之后,顺利保送pku.这一天,抖儿问长者:"我虽然已经保送了,但我的志向是为国家健康工作五十年.请问我应该怎样变得更有力气?"  长者回答:"你啊,Too Young Too Simple,Sometimes Naive!如果你想要我教你,你要先进行艰苦的修行." 长者的住宅中有一堵长度为n的墙.每天抖儿起床修行,会选择一段长度为x的区间染成白色.长者的住宅附近有一群香港记者,为了借助抖儿拜访长者,第i天香港记者会将区间[li,ri]

UOJ【UR #12】实验室外的攻防战

题意: 给出一个排列$A$,问是否能够经过以下若干次变换变为排列$B$ 变换:若${A_i> A_i+1}$,可以${swap(A_i,A_i+1)}$ 考虑一个数字从A排列到B排列连出来的路径与其他数字是否相交,相交就表示大小关系需要判断,(类似于二维偏序)用线段树维护区间最小值即可. 权值为1,2的线分别与权值为4的线相交,而且4在它们左边,所以需要判断它们的大小关系,发现${4>1}$,${4>2}$,所以满足条件. 1 #include<iostream> 2 #in

uoj#62 怎样跑的更快 (莫比乌斯反演)

题目链接:http://uoj.ac/problem/62 推式子呀推式子 发现我对莫比乌斯反演一无所知qaq 预处理出要用的数组,然后反演反演反演就好啦 #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<queue> #include<stack> #include<cmath> using namespa

[UOJ #180][UR #12]实验室外的攻防战(树状数组)

Description 时针指向午夜十二点,约定的日子——2月28日终于到来了.随着一声枪响,伏特跳蚤国王率领着他的跳蚤大军们包围了 picks 博士所在的实验室. 当然,picks 博士不会坐以待毙,他早就率领着他的猴子们在实验室外修筑了许多的坚固防御工事. 经过跳蚤侦察兵的勘察,跳蚤国王发现 picks 博士的防御工事有着 n 处薄弱点,于是他把他的跳蚤大军分成了 n 支小队,并打算让它们分别进攻每一个薄弱点.但是因为战场混乱,这 n 支小队的位置被打乱了,重新整队之后,跳蚤国王发现第 i

论触摸板是比鼠标更先进的输入方式

大约半年之前,我一直在用鼠标.后来因为手指疼,以及经常需要在鼠标和键盘之间切换,所以逐渐开始尝试使用触摸板代替鼠标.现在我已经完全不再用鼠标,而是全部用触摸板+键盘工作.在这个过程中,我体会到,在绝大部分情况下,触摸板都比鼠标便捷,至少不比鼠标差.因此用这篇文章和大家分享一下这个过程中的体会. 触摸板的输入方式 首先介绍一下我用的笔记本电脑上触摸板的输入方式.一个是Thinkpad E330,它的触摸板前方有三个键,大致分别对应于鼠标的左右键和滚轮,而触摸板本身也可以按下去,相当于单击左键.另外

深度强化学习泡沫及路在何方?

一.深度强化学习的泡沫 2015年,DeepMind的Volodymyr Mnih等研究员在<自然>杂志上发表论文Human-level control through deep reinforcement learning[1],该论文提出了一个结合深度学习(DL)技术和强化学习(RL)思想的模型Deep Q-Network(DQN),在Atari游戏平台上展示出超越人类水平的表现.自此以后,结合DL与RL的深度强化学习(Deep Reinforcement Learning, DRL)迅速

一年之计在于春-2015年两会-国家总理李克强答记者问

首先需要声明,本文纯属一个毫无远见和真才实学的小小散户的愚昧见解,仅供参考. 学习心得 金融市场会进一步开放,政府不会再扮演保姆的角色了,盈亏都是大家的决定,自己承担后果: 在金融风险上,中国的确是一个个案性的金融风险,但是我们完全可以守住,不发生系统性我们允许个案性金融风险的发生,按照市场方式进行清算.今年我们要出台存款保险条例,而且要进一步发展多层次资本市场,降低企业资金杠杆率,可以使金融更好为企业服务.(银行可以倒闭,混业经营,注册制,金融改革等): 我很愿意为网购.快递和带动的电子商务等