题解:P14013 [POCamp 2023] 送钱 / The Generous Traveler

隔了2天终于写完了。

题目链接
my team

前置知识:树剖。

思路:

我们先看一下一个数字连续取模多个数字要怎么办?

采用暴力法一个个算肯定是不可取的,如果我们认真观察就会发现对于 a a a m o d mod mod b b b 来说当 a < b a < b a<b 时,取模结果依然是 a a a 。所以每次取模完只需要找出下一个小于 a a a的模数取模即可。尽管我一开始就想到这个方法,但是我以为这样会超时,知道我看到了某篇题解后才发现,当一个数被一个小于它的数取模完之后结果必然小于原数的一半,也就是说取模次数不超过 log ⁡ 2 n \log_2n log2n 次(n为被取模数字),如果用树剖去做时间复杂度只是 O ( q log ⁡ 2 n log ⁡ 2 x ) O(q\log_2n\log_2x) O(qlog2nlog2x)。

知道了如何取模操作之后剩下的就全是树剖模板了,只需维护一棵线段树用来查找一段区间第一个小于或大于 x x x 的数即可。

值得注意得是取模没有交换律,所以得要老老实实的把 u u u 和 v v v 跳过的区间分别存贮存起来。

代码

cpp 复制代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,deep[200005],q,c[200005],SIZE[200005],son[200005],father[200005],dfscnt,id[200005],top[200005],HASH[200005],MIN[800005];
vector<int>e[200005];
void dfs_1(int u,int fa){
	father[u]=fa;
	SIZE[u]=1;
	deep[u]=deep[fa]+1;
	for(size_t i=0;i<e[u].size();i++){
		int v=e[u][i];
		if(v==fa)continue;
		dfs_1(v,u);
		SIZE[u]+=SIZE[v];
		if(SIZE[v]>SIZE[son[u]])
		son[u]=v;
	}
}
void dfs_2(int u,int topx){
	top[u]=topx;
	id[u]=++dfscnt;
	HASH[dfscnt]=u;
	if(!son[u]) return;
	dfs_2(son[u],topx);
	for(size_t i=0;i<e[u].size();i++){
		int v=e[u][i];
		if(v==father[u]||v==son[u])continue;
		dfs_2(v,v);
	}
}
void build(int l,int r,int k){
	if(l==r){
		MIN[k]=c[HASH[l]];
		return;
	}
	int mid=(l+r)/2;
	build(l,mid,2*k);
	build(mid+1,r,2*k+1);
	MIN[k]=min(MIN[2*k],MIN[2*k+1]);
}
int find_front(int L,int R,int x,int l,int r,int k){
	if(l==r)return MIN[k];
	int mid=(l+r)/2,res=0;
	if(L<=mid&&MIN[2*k]<=x) res=find_front(L,R,x,l,mid,2*k);
	if(R>=mid+1&&MIN[2*k+1]<=x&&res==0) return find_front(L,R,x,mid+1,r,2*k+1);
	return res;
}
int find_back(int L,int R,int x,int l,int r,int k){
	if(l==r){
		return MIN[k];
	}
	int mid=(l+r)/2,res=0;
	if(R>=mid+1&&MIN[2*k+1]<=x) res=find_back(L,R,x,mid+1,r,2*k+1);
	if(L<=mid&&MIN[2*k]<=x&&res==0) return find_back(L,R,x,l,mid,2*k);
	return res;
}
struct node{
	int l,r;
};
int query(int u,int v,int x){
	vector<node>qu;
	vector<node>qv;
	while(top[u]!=top[v]){
		if(deep[top[u]]<deep[top[v]]){
			qv.push_back({id[top[v]],id[v]});
			v=father[top[v]];
		}else{
			qu.push_back({id[top[u]],id[u]});
			u=father[top[u]];
		}
	}
	if(id[u]<id[v])qv.push_back({id[u],id[v]});
	else qu.push_back({id[v],id[u]});
	for(int i=0;i<qu.size();i++){
		int l=qu[i].l,r=qu[i].r;
		while(1){
			int mod=find_back(l,r,x,1,n,1);
			if(mod==0)break;
			x%=mod;
		}
	}
	for(int i=qv.size()-1;i>=0;i--){
		int l=qv[i].l,r=qv[i].r;
		while(1){
			int mod=find_front(l,r,x,1,n,1);
			if(mod==0)break;
			x%=mod;
		}
	}
	return x;
}
signed main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>n>>q;
	for(int i=1;i<=n-1;i++){
		int u,v;
		cin>>u>>v;
		e[u].push_back(v);
		e[v].push_back(u);
	}
	for(int i=1;i<=n;i++)
	cin>>c[i];
	dfs_1(1,1);
	dfs_2(1,1);
	build(1,n,1);
	while(q>0){
		q--;
		int u,v,x;
		cin>>u>>v>>x;
		cout<<query(u,v,x)<<endl;
	}
	return 0;
}
相关推荐
布莱克6053 分钟前
数组详解:定义、作用、应用场景及与链表的区别(C/C++ 代码讲解)
c语言·开发语言·c++·数组
hongmai66688816 分钟前
MP2348GTL-Z:这颗24V/4A同步降压芯片,把性能和体积平衡得恰到好处
c语言·开发语言·arm开发·单片机·5g
多弗朗皮卡丘20 分钟前
C语言梦开始的地方19:文件操作
c语言·开发语言
Dovis(誓平步青云)20 分钟前
折叠屏悬停看视频,上半屏和下半屏应该各做什么
android·java·服务器·开发语言·安全·音视频
benchmark_cc31 分钟前
Python量化实战:如何检测并剔除历史数据中的“闪崩/乌龙指”异常价格点?
开发语言·人工智能·python·量化·quantdash·量化数据源
2501_9378609435 分钟前
Java多线程初阶(下)—— synchronized、volatile、wait/notify与经典并发工具
java·开发语言·jvm
西西弗Sisyphus1 小时前
模型训练 不同批次大小与学习率下的训练损失对比(2)
学习·batchsize·lr·learning rate
笨笨饿1 小时前
#135_代码中的类型重定义艺术:从基础封装到函数类型设计
开发语言·stm32·单片机·嵌入式硬件·mcu·物联网·嵌入式实时数据库
统计学小王子1 小时前
R语言入门——ggplot2图例增加公式
开发语言·r语言
牛油果子哥q1 小时前
C++内存池与对象池精讲:内存碎片、自定义分配器、对象池实现、STL allocator原理、工程落地与性能对比
java·开发语言·c++