题解: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;
}
相关推荐
froginwe1112 分钟前
CSS 简介
开发语言
叫我一声阿雷吧21 分钟前
JS实现无限滚动加载列表|适配多端+性能优化【附完整可复用源码】
开发语言·javascript·性能优化
CappuccinoRose1 小时前
CSS 语法学习文档(十五)
前端·学习·重构·渲染·浏览器
qq_24218863321 小时前
影视工厂渲染优化指南大纲
经验分享
汉克老师1 小时前
GESP2024年6月认证C++二级( 第三部分编程题(1) 平方之和)
c++·算法·预处理·完全平方数·循环结构·gesp二级·gesp2级
StandbyTime1 小时前
《算法笔记》练习记录-2.5-问题 C: 习题6-6 杨辉三角
c++·算法笔记
MediaTea1 小时前
Python:可迭代对象(对象语义角色)
开发语言·python
lsx2024061 小时前
NumPy 线性代数
开发语言
学习是生活的调味剂1 小时前
nacos原理之服务注册浅析
java·开发语言·nacos·注册中心
MR_Promethus2 小时前
【C++11】condition_variable 条件变量
c++·条件变量·并发编程