题解: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;
}
相关推荐
橙露2 小时前
C#在视觉检测中的优势:工业智能化转型的利器
开发语言·c#·视觉检测
醇氧2 小时前
java.lang.NumberFormatException: For input string: ““
java·开发语言·spring
小裕哥略帅2 小时前
PMP学习笔记--过程
笔记·学习
利刃大大2 小时前
【ES6】变量与常量 && 模板字符串 && 对象 && 解构赋值 && 箭头函数 && 数组 && 扩展运算符 && Promise/Await/Async
开发语言·前端·javascript·es6
天赐学c语言2 小时前
1.18 - 滑动窗口最大值 && 子类的指针转换为父类的指针,指针的值是否会改变
数据结构·c++·算法·leecode
大猫会长2 小时前
postgreSQL中,RLS的using与with check
开发语言·前端·javascript
好奇龙猫2 小时前
【人工智能学习-AI入试相关题目练习-第六次】
人工智能·学习
老蒋每日coding2 小时前
Python:数字时代的“万能钥匙”
开发语言·python
是娇娇公主~2 小时前
C++集群聊天服务器(3)—— 项目数据库以及表的设计
服务器·数据库·c++