题解: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;
}
相关推荐
寻星探路9 小时前
【深度长文】万字攻克网络原理:从 HTTP 报文解构到 HTTPS 终极加密逻辑
java·开发语言·网络·python·http·ai·https
lly20240610 小时前
Bootstrap 警告框
开发语言
2601_9491465311 小时前
C语言语音通知接口接入教程:如何使用C语言直接调用语音预警API
c语言·开发语言
曹牧11 小时前
Spring Boot:如何测试Java Controller中的POST请求?
java·开发语言
在路上看风景11 小时前
19. 成员初始化列表和初始化对象
c++
KYGALYX11 小时前
服务异步通信
开发语言·后端·微服务·ruby
zmzb010311 小时前
C++课后习题训练记录Day98
开发语言·c++
执笔论英雄11 小时前
【大模型学习cuda】入们第一个例子-向量和
学习
wdfk_prog11 小时前
[Linux]学习笔记系列 -- [drivers][input]input
linux·笔记·学习
念风零壹12 小时前
C++ 内存避坑指南:如何用移动语义和智能指针解决“深拷贝”与“内存泄漏”
c++