首页 > 其他分享 >230718B3-path 题解

230718B3-path 题解

时间:2024-02-02 21:13:03浏览次数:29  
标签:dj int 题解 230718B3 mid son dfn inline path

感谢 cn_ryh 大佬的怂恿(否则我真不会动这个题

感谢 cszhpdx 的指导帮助 qwq

(让我们膜拜一下场切的浩杨哥 orz

解决这个题让人很有成就感(

题意

给定一个基环树,边有长度l、限速v、价值w(每单位时间)

已知起点s、终点t、最高速度u,求最小花费

边数、询问次数 $10^5$

解法

首先学习一下基环树,在这个题里环对答案的影响就是让路径多了一条。

考虑环上的任意一边,两条路径一定分别经过和不经过这条边。

  • 不经过:断掉这条边,让基环树变成树。
  • 强制经过:让起终点分别到两端点,再加上这条边本身。

现在问题转化为在树上求两点间路径,计算花费

众所不周知树链剖分就是干这个用的

由于树剖保证同一条重链上dfs序连续,这很明显就是区间查询,所以可以在dfs序上建线段树等数据结构。

显然对于每条边,为了花费最小,要求时间最短,速度最大。只有 2 种情况:按照 v 跑和按照 u 跑( $V=\min(u,v)$ )。

那么我们建两棵线段树,分别记录当前按v跑的总花费,按u跑的总花费,查询的时候加起来就好了(


大体的思路就是上面说的这样qwq

有几个地方要注意:

  1. 边上有权而不是点,可以把权值转移到深度较深的点上,计算的时候注意 lca 对应的权值不算

  2. 有多个询问,可以离线下来,按u排序,这样每次只需要修改跟上次不一样的点(按v排序),把操作次数降到 $O(n)$。

  3. 起点和终点重合的情况最好加个特判

  4. 记得开 long long


流程:

  1. 输入,建边

  2. dfs 找环+树剖

  3. 建线段树

  4. 询问排序,点排序

  5. 每次修改线段树,计算答案


这题代码长达 5k,细节还是挺多的(尤其是点排序后很多下标会变化,要想清楚)

还是放一下完整代码纪念我写过最长的题qwq

#include <bits/stdc++.h>
using namespace std;
#define rep(i,n) for(int i=1;i<=n;i++)
#define rep0(i,n) for(int i=0;i<n;i++)
#define drep(i,n) for(int i=n;i>=1;i--)
#define REPG(i,h,x) for(int i=h[x];~i;i=e[i].next)
#define LL long long

inline int read(){
	int s=0,f=1;char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
	for(;isdigit(c);c=getchar()) s=s*10+c-'0';
	return s*f;
}

const int N=1e5+50;

int head[N],cnt;
struct edge{
	int to,next,len,dj;
}e[2*N];
inline void add(int x,int y,int l,int dj){
	e[++cnt]=(edge){y,head[x],l,dj};head[x]=cnt;
}

int tot;
int rkp[N],rkd[N];

struct point{
	int fa,dep,siz,son;
	int len,dj;
	int top,dfn;
	int id;
	inline bool operator<(const point &a) const{
		return dj>a.dj;
	}
}p[N];


struct duan{
	int a,b,l,x;
}d;

inline void dfs_c(int u){
	p[u].son=-1;p[u].siz=1;
	REPG(i,head,u){
		int v=e[i].to;
		if(v==p[u].fa||(u==d.a&&v==d.b)) continue;
		if(p[v].dep){
			d.a=u,d.b=v;
			d.l=e[i].len,d.x=e[i].dj;
		}
		else{
			p[v].fa=u;
			p[v].dep=p[u].dep+1;
			p[v].len=e[i].len,p[v].dj=e[i].dj;
			dfs_c(v);
			p[u].siz+=p[v].siz;
			if(p[u].son==-1||p[v].siz>p[p[u].son].siz) p[u].son=v;
		}
	}
}

inline void dfs_p(int u,int t){
	p[u].top=t;
	p[u].dfn=++tot;
	rkd[tot]=u;
	if(p[u].son==-1) return;
	dfs_p(p[u].son,t);
	REPG(i,head,u){
		int v=e[i].to;
		if((u==d.a&&v==d.b)||(u==d.b&&v==d.a)) continue;
		if(v!=p[u].son&&v!=p[u].fa) dfs_p(v,v);
	}
}

struct SegmentTree{
	int cnt;
	int ls[2*N],rs[2*N];
	double sum[2*N];
	
	inline int build(int l,int r){
		int x=++cnt;sum[x]=0;
		if(l==r) return x;
		int mid=(l+r)>>1;
		ls[x]=build(l,mid);
		rs[x]=build(mid+1,r);
		return x;
	}
	
	inline void pushup(int x){
		sum[x]=sum[ls[x]]+sum[rs[x]];
	}
	inline void change(int x,int l,int r,int p,double v){
		if(l==r) return sum[x]=v,void();
		int mid=(l+r)>>1;
		if(mid>=p) change(ls[x],l,mid,p,v);
		else change(rs[x],mid+1,r,p,v);
		pushup(x);
	}
	
	inline double query(int x,int l,int r,int s,int t){
		if(l>=s&&r<=t) return sum[x];
		double res=0;
		int mid=(l+r)>>1;
		if(mid>=s) res+=query(ls[x],l,mid,s,t);
		if(t>mid) res+=query(rs[x],mid+1,r,s,t);
		return res;
	}
}t1,t2;

struct question{
	int s,t,u;
	int id;
	inline void rd(){
		s=read(),t=read(),u=read();
	}
	inline bool operator< (const question &a)const{
		return u<a.u;
	}
}q[N];
double ans[N];

int n,m,Q;
int v[N],w[N];

inline double dis(int x,int y,int u){
	if(x==y) return 0;
	double res=0;x=rkp[x],y=rkp[y];
	int fx=rkp[p[x].top],fy=rkp[p[y].top];
	while(fx!=fy){
		if(p[fx].dep>=p[fy].dep)
			res+=t1.query(1,1,n,p[fx].dfn,p[x].dfn)+t2.query(1,1,n,p[fx].dfn,p[x].dfn)/u,
			x=rkp[p[fx].fa];
		else
			res+=t1.query(1,1,n,p[fy].dfn,p[y].dfn)+t2.query(1,1,n,p[fy].dfn,p[y].dfn)/u,
			y=rkp[p[fy].fa];
		fx=rkp[p[x].top];fy=rkp[p[y].top];
	}
	if(p[x].dfn<p[y].dfn)
		res+=t1.query(1,1,n,p[rkp[p[x].son]].dfn,p[y].dfn)+t2.query(1,1,n,p[rkp[p[x].son]].dfn,p[y].dfn)/u;
	else
		res+=t1.query(1,1,n,p[rkp[p[y].son]].dfn,p[x].dfn)+t2.query(1,1,n,p[rkp[p[y].son]].dfn,p[x].dfn)/u;
	return res;
}

int main(){
	memset(head,-1,sizeof(head));
	n=read(),m=read(),Q=read();
	rep(i,n){
		int a,b,l,x;
		a=read(),b=read(),l=read(),x=read();
		add(a,b,l,x);
		add(b,a,l,x);
	}
	p[1].dep=1;dfs_c(1);
	dfs_p(1,1);
	
	rep(i,m){
		v[i]=read(),w[i]=read();
	}
	
	rep(i,Q){
		q[i].rd();
		q[i].id=i;
	}
	sort(q+1,q+Q+1);
	
	rep(i,n) p[i].id=i;
	sort(p+1,p+n+1);
	rep(i,n) rkp[p[i].id]=i;
	
	int rt=t1.build(1,n);
	rt=t2.build(1,n);
	rep(i,n){
		t2.change(rt,1,n,i,(LL)p[rkp[rkd[i]]].len*w[p[rkp[rkd[i]]].dj]);
	}
	int lst=1;
	rep(i,Q){
		int u=q[i].u;
		while(v[p[lst].dj]<u&&lst<=n){
			t1.change(rt,1,n,p[lst].dfn,(double)p[lst].len/v[p[lst].dj]*w[p[lst].dj]);
			t2.change(rt,1,n,p[lst].dfn,0);
			lst++;
		}
		double res=dis(q[i].s,q[i].t,u);
		res=min(res,dis(q[i].s,d.a,u)+dis(q[i].t,d.b,u)+(double)d.l/min(v[d.x],u)*w[d.x]);
		res=min(res,dis(q[i].s,d.b,u)+dis(q[i].t,d.a,u)+(double)d.l/min(v[d.x],u)*w[d.x]);
		ans[q[i].id]=res;
	}
	rep(i,Q) printf("%.2lf\n",ans[i]);
	return 0;
}

完结撒花 qwq

标签:dj,int,题解,230718B3,mid,son,dfn,inline,path
From: https://www.cnblogs.com/Cindy-Li/p/18003997

相关文章

  • Poj3126 Prime Path (BFS+筛素数)
    #include<iostream>#include<queue>#include<cstring>constintN=10010;intt,aa,bb,prime[N],vis[N],step[N];usingnamespacestd;voidprime_(){//埃式筛法prime[0]=prime[1]=1;for(inti=2;i<10000;i++){if(prime[i])contin......
  • 基于客户真实使用场景的云剪辑Timeline问题解答与代码实操
    本文为阿里云智能媒体服务IMS「云端智能剪辑」实践指南第6期,从客户真实实践场景出发,分享一些Timeline小技巧(AI_TTS、主轨道、素材对齐),助力客户降低开发时间与成本。欧叔|作者故事的开始要从一条客户的真实反馈说起。  Round1:视频比音频长,怎么办?某天,一位客户加入了智能媒......
  • 盘点那些硬件+项目学习套件:Hi3861鸿蒙开发板及入门常见问题解答
    华清远见20岁了~过去3年里,华清远见研发中心针对个人开发板业务,打造了多款硬件+项目学习套件,涉及STM32单片机、嵌入式、物联网、人工智能、鸿蒙、ESP32、阿里云IoT等多技术方向。今天我们来盘点一下,比较受欢迎几款“硬件+项目”学习套件,以及一些初学者比较关注的问题。盘点二:Hi3861......
  • fatal: couldn't find remote ref master 问题解决!
    这个错误信息通常出现在使用Git命令尝试从远程仓库克隆、拉取(pull)或推送(push)时,指定的分支(在这个案例中是master)在远程仓库中不存在。这种情况可能由以下几个原因导致:1.分支名称错误远程仓库中不存在名为master的分支:随着Git和GitHub的更新,master分支被重新命名为main......
  • AcWing 520. 子串 题解
    ps:觉得这编号很特殊就做了一下题目传送门算法(线性DP,前缀和)\(O(nmk)\)首先考虑如何DP,然后再考虑如何优化。状态表示:f[i,j,k]表示只用S的前i个字母,选取了k段,可以匹配T的前j个字母的方案数。状态计算:将f[i,j,k]表示的所有方案分成两大类:不用S[i],则方案数是f[i-1,......
  • P9309 [EGOI2021] Number of Zeros题解
    模拟赛时以为是进位制的题目,结果还做出来了。此题解解法与其它相似,但观察的角度不同(作者的脑回路不同)。此题问\(a\simb\)的最小公倍数中后导\(0\)的个数,即求其中\(2\)和\(5\)个数的最小值。分别计算即可,想到进位制,以\(a=10\),\(b=12\)时\(2\)的个数为例:1001(9)......
  • P9612 [CERC2019] Light Emitting Hindenburg 题解
    题目传送门题目大意这个题目简化一下就是求\(n\)个数中取\(k\)个数按位与的最大值思路很容易想到贪心。题中说道输入的数在二进制下最多\(29\)位,所以我们从\(29\)开始遍历二进制位,如果当前位有大于等于\(k\)个\(1\),那么标记一下这些数,可以发现剩下的比当前位低的......
  • AcWing 3721. 求30的倍数 题解
    传送门stoi这里我们来了解一个函数\(stoi\)作用是将\(n\)进制的字符串转化为十进制,使用时包含头文件\(string\)定义如下:intstoi(conststd::string&str,std::size_t*pos=nullptr,intbase=10);参数:\(str\)-待转换的字符\(pos\)-其取值可以是一个空字......
  • CF1687D Cute number 题解
    题目链接点击打开链接题目解法一个数\(c\)是可爱(下文称为合法)的,当且仅当\(c\in[x^2,x(x+1)]\)令\(a_i\)属于\([x_i,x_i(x_i+1)]\)一个显然的范围是\(x_1\le2\times10^6\)考虑枚举\(x_1\)现在说明一个结论:\(x_1\)固定时,\(x_i\)也固定了说明:不难发现,合法的不合......
  • CF620E New Year Tree 题解
    题目链接:CF或者洛谷这题很简单,看到颜色数,HH的项链?树,树上的HH的项链?带修,树上的镜中的昆虫?\(c_i\le60\),噢,easy了。考虑一类信息,表示有和无,对于某种颜色来讲,\(0/1\)表示无或者有,而或运算让我们从小区间的有无情况反映到大区间的有无情况。一种暴力的想法,为每种颜色建立一棵有......