题解: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;
}
相关推荐
神仙别闹3 小时前
基于 C++ 实现(控制台)学生成绩管理系统
开发语言·c++
会周易的程序员3 小时前
5 节点边缘冗余方案(上):基于 aiRaft 的物联网高可用控制面设计
c++·分布式·物联网·raft·iot·共识
2601_962218903 小时前
物联网对接技术视角下万象生鲜配送系统配送效率优势
其他
伞伞悦读4 小时前
【第36期】Python 目录与路径详解:pathlib、文件遍历、创建、复制、移动和删除风险
开发语言·python
智者知已应修善业5 小时前
【4060BD_5V应用电路图】2022-12-24
驱动开发·经验分享·笔记·硬件架构·硬件工程
小弥儿6 小时前
GitHub今日热榜 | 2026-09-18:腾讯双项目上榜
学习·开源
qq_199886876 小时前
第6板块·第4节:构建系统与多文件项目
c++·人工智能·gpu算力
2601_966949657 小时前
只拿到股票代码还不够:量化程序到底还需要哪些标的信息?——从数据建模开始理解股票元数据
开发语言·python·数据分析·pandas·量化交易·股票数据·quantdash
m0_734571767 小时前
深入理解5g <十八>传输块tbsize的计算
开发语言·5g
知识分享小能手8 小时前
深度学习学习教程,从入门到精通,深度生成模型 —— 知识点详解与代码实现(20)
人工智能·深度学习·学习