P4004 Hello world!

文章介绍了如何利用并查集和树状数组高效处理数论问题中的修改和询问操作,通过合理划分和层次结构减少操作次数,从而优化了时间复杂度至O(Q*N/α+6NL*α)。

【思路要点】

一个 101310^{13}1013 以内的数开根 666 次后一定会变成 111,因此有效的修改次数不会超过 6N6N6N

设定一个阈值 α\alphaα,若 k≥αk\ge\alphakα,则暴力进行询问或修改,借助长链剖分求 kkk 级祖先,单次操作时间复杂度为 O(Nα)O(\frac{N}{\alpha})O(αN)

考虑 k<αk\lt\alphak<α 的修改,用并查集维护每个点向上 kkk 的整数倍步第一个能够到达的非 111 的位置,再进行修改,就可以保证找到的位置上的修改有效。并查集大小为 O(Nα)O(N\alpha)O(Nα)

考虑 k<αk\lt\alphak<α 的询问,用 α\alphaα树状数组维护子树和即可快速查询。

时间复杂度 O(Q∗Nα+6NLogN∗α)O(\frac{Q*N}{\alpha}+6NLogN*\alpha)O(αQN+6NLogNα)

【代码】

//Serene
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
using namespace std;
#define ll long long
#define db double
#define For(i,a,b) for(int i=(a);i<=(b);++i)
#define Rep(i,a,b) for(int i=(a);i>=(b);--i)
const int maxn=1e5+7,maxs=200+7,maxt=23,W=19;
ll n,m,a[maxn],s;
 
char cc;ll ff;
template<typename T>void read(T& aa) {
    aa=0;cc=getchar();ff=1;
    while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
    if(cc=='-') ff=-1,cc=getchar();
    while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
    aa*=ff;
}
 
int fir[maxn],nxt[maxn],to[maxn],e=0;
void add(int x,int y) {
    to[++e]=y;nxt[e]=fir[x];fir[x]=e;
    to[++e]=x;nxt[e]=fir[y];fir[y]=e;
}
 
int f[maxn];
int find(int x) {return x==f[x]? x:f[x]=find(f[x]);}
 
int fa[maxn][maxt],jp[maxn][maxs],dep[maxn];
void dfs(int pos,int f) {
    int y,z; dep[pos]=dep[f]+1;
    fa[pos][0]=jp[pos][1]=f; jp[pos][0]=pos;
    For(i,1,W) fa[pos][i]=fa[fa[pos][i-1]][i-1];
    For(i,2,s) jp[pos][i]=jp[f][i-1];
    for(y=fir[pos];y;y=nxt[y]) {
        if((z=to[y])==f) continue;
        dfs(z,pos);
    }
}
 
inline int get_fa(int x,int k) {
    if(k<=s) return jp[x][k];
    Rep(i,W,0) if(k>=(1<<i)) {x=fa[x][i]; k-=(1<<i);}
    return x;
}
 
int get_lca(int x,int y) {
    if(dep[x]!=dep[y]) {
        if(dep[x]<dep[y]) swap(x,y);
        Rep(i,W,0) if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
    }
    if(x==y) return x;
    Rep(i,W,0) if(fa[x][i]!=fa[y][i]) {x=fa[x][i]; y=fa[y][i];}
    return fa[x][0];
}
 
void chge(int x) {
    if(a[x]==1) return;
    a[x]=floor(sqrt(a[x]));
    if(a[x]==1) f[x]=find(fa[x][0]);
}
 
int q(int x,int y,int lca,int k) {
    if(dep[y]-dep[lca]>=k) return get_fa(y,k);
    k-=dep[y]-dep[lca]; y=lca;
    return get_fa(x,dep[x]-dep[y]-k);
}
 
inline int lst(int x,int k) {
    if(k>s) return get_fa(x,k);
    int y=find(fa[x][0]);
    int p=(dep[x]-dep[y])%k;
    if(p) p=k-p;
    return jp[y][p];
}
 
void get_chge(int x,int y,int k) {
    int lca=get_lca(x,y),len=dep[x]+dep[y]-2*dep[lca];
    if(len%k) chge(y),y=q(x,y,lca,len%k),lca=get_lca(x,y);
    while(dep[x]>=dep[lca]) {chge(x); x=lst(x,k);}
    while(dep[y]>dep[lca]) {chge(y); y=lst(y,k);}
}
 
ll Yth(int x,int y,int k) {
    int lca=get_lca(x,y),len=dep[x]+dep[y]-2*dep[lca]; ll rs=0;
    if(len%k) rs+=a[y],y=q(x,y,lca,len%k),lca=get_lca(x,y);
    rs+=(dep[x]+dep[y]-2*dep[lca])/k+1;
    while(dep[x]>=dep[lca]) {rs+=a[x]-1; x=lst(x,k);}
    while(dep[y]>dep[lca]) {rs+=a[y]-1; y=lst(y,k);}
    return rs;
}
 
int main() {
    read(n); int op,x,y,k;
    s=min(200,(int)sqrt(n)+1);
    For(i,1,n) read(a[i]),f[i]=i;
    For(i,1,n-1) {
        read(x); read(y);
        add(x,y);
    }
    dfs(1,0); read(m);
    For(i,1,n) if(a[i]==1) f[i]=fa[i][0];
    For(i,1,m) {
        read(op); read(x); read(y); read(k);
        if(op==0) get_chge(x,y,k);
        else printf("%lld\n",Yth(x,y,k));
    }
    return 0;
}
ROS 中涉及的编程语言以 C++ 和 Python 为主,实现输出“Hello World!”的流程大致为:创建一个工作空间;创建一个功能包;编辑源文件;编辑配置文件;编译并执行 [^3]。以下为具体操作: ### 创建工作空间并初始化 ```bash mkdir -p 自定义空间名称/src cd 自定义空间名称 catkin_make ``` ### 创建功能包 进入工作空间下的 `src` 文件夹,创建 `hello_world_pkg` 功能包 [^2]。 ### 编辑源文件 在 `hello_world_pkg` 功能包下创建 `hello_world.cpp` 文件,并写入以下代码 [^2]: ```cpp #include "ros/ros.h" int main(int argc,char *argv[]){ ros::init(argc,argv,"hello_world"); //初始化ros节点 ROS_INFO("hello world"); //输出hello world return 0; } ``` 也可以在功能包的 `src` 下创建 `cpp` 文件,参考代码如下 [^4]: ```cpp #include "ros/ros.h" int main(int argc,char *argv[]) { //解决乱码问题 setlocale(LC_ALL,""); ros::init(argc,argv,"helloworld"); //节点初始化 ROS_INFO("hello world!,哈哈哈"); //输出日志 return 0; } ``` 还可以创建代码文件,内容如下 [^1]: ```cpp #include "ros/ros.h" int main(int argc, char *argv[]) { //执行 ros 节点初始化 ros::init(argc,argv,"hello"); //创建 ros 节点句柄(非必须) ros::NodeHandle n; //控制台输出 hello world ROS_INFO("hello world!"); return 0; } ``` ### 编辑配置文件 返回上一级,编辑 ros 包下的 `Cmakelist.txt` 文件 [^1]。 ### 编译并执行 编译工作空间,在工作空间根目录下执行 `catkin_make`。编译成功后,在新的终端中启动 ROS 核心:`roscore`,再在另一个终端中运行节点:`rosrun hello_world_pkg hello_world`(根据实际功能包和节点名调整)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值