CF463E Caisa and Tree
树链剖分真可爱。
给出 \(n\) 个点以 \(1\) 为根的树,点 \(u\) 有点权 \(a_u\)。\(m\) 次操作,两种类型:
\(1\texttt{ }u\),查询点 \(u\) 的一个最近祖先 \(v\),满足 \(\gcd(a_u,a_v)>1\),没有输出 \(-1\)。
\(2\texttt{ }u\texttt{ }d\),将 \(a_u\leftarrow d\)。
\(n,m\le 10^5\),\(a_u\le 2\times 10^6\)。
「保证 \(2\) 操作的数量不超过 \(50\)」的限制和 \(10\) 秒的时限太不牛了。让我们去掉这个限制并将时限改为 \(2\) 秒,再考虑怎么做。
看到单点修改和路径查询,容易想到树链剖分。考虑到 \(\gcd(a_u,a_v)>1\) 本质上是 \(a_u,a_v\) 有着共同的质因数,所以要先用欧拉筛筛出 \(2\times 10^6\) 内的质数。然后可以枚举这个质因数,分别查询有该质因数的最近祖先,再从其中选出最近的即可。
由于根到某个点路径的 \(dfn\) 序是递增的,考虑通过求最大的 \(dfn\) 序来找到最近祖先。具体地,对每种质数维护一个 set,里面有序存放点权有该质因数的点的 \(dfn\) 序。查询某种质因数时,在跳链的过程中通过 lower_bound 和 upper_bound 二分找到不超过当前点 \(u\) 的最大 \(dfn\) 序(如果 \(u\) 为初始点则是严格小于,因为自己不能选),若该值 \(\ge dfn_{top_u}\),说明该值对应的点在当前重链上,直接返回即可。
对于修改操作,相当于在 \(a_u\) 的质因数的 set 中删除 \(dfn_u\),在 \(y\) 的质因数的 set 中插入 \(dfn_u\)。
乍一看分解质因数很暴力,但是我们可以在欧拉筛的时候处理每个数 \(i\) 的最小质因数 \(pre_i\),分解质因数的时候,设当前数为 \(x\),则分解一个 \(pre_{x}\) 出来,再令 \(x\leftarrow \dfrac{x}{pre_{x}}\)。由于质数 \(\ge 2\),因此除以 \(pre_x\) 的操作至多进行 \(\log x\) 次,意味着我们完成了在 \(\mathcal{O}(\log x)\) 的时间复杂度内分解质因数。而且,\(2\times 10^6\) 内的数本质不同的质因数个数不超过 \(7\),考虑 \(2\times 3\times 5\times 7\times 11\times 13\times 17\times 19=9699690\),而我们对于同一个质因数只需要进行一次跳链查询/修改 set,因此单次操作跳链查询/修改 set 次数是常数级别。
设值域为 \(V\),最终的时间复杂度为 \(\mathcal{O}(m\log^2 n)\),空间复杂度为 \(\mathcal{O}(|V|)\)。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5,V=2e6+5;
int n,m,a[N],pr[V],tot,cnt,pre[V],siz[N],fa[N],hvy[N],top[N],dfn[N],id[N];
bool notp[V],vis[V];
vector<int>g[N];
set<int>s[V];
template<typename T>void read(T&x){
x=0;T f=1;char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=(x<<3)+(x<<1)+c-48;
x*=f;
}
template<typename T>void write(T x){
if(x>9)write(x/10);
putchar(x%10+48);
}
template<typename T>void print(T x,char ed='\n'){
if(x<0)putchar('-'),x=-x;
write(x),putchar(ed);
}
void reVis(int val){
for(int tmp=val;tmp!=1;tmp/=pre[tmp])vis[pre[tmp]]=0;
}
void dfs1(int u){
++siz[u];
for(int v:g[u]){
if(v==fa[u])continue;
fa[v]=u,dfs1(v),siz[u]+=siz[v];
}
}
void dfs2(int u){
for(int v:g[u]){
if(v==fa[u])continue;
if((siz[v]<<1)>siz[u])top[hvy[u]=v]=top[u];
else top[v]=v;
dfs2(v);
}
}
void dfs3(int u){
id[dfn[u]=++tot]=u;
for(int tmp=a[u];tmp!=1;tmp/=pre[tmp]){
if(!vis[pre[tmp]])vis[pre[tmp]]=1,s[pre[tmp]].insert(tot);
}
reVis(a[u]);
if(hvy[u])dfs3(hvy[u]);
for(int v:g[u]){
if(v==fa[u]||v==hvy[u])continue;
dfs3(v);
}
}
int query(int x,int fac){
int u=x;
while(u){
auto it=(u==x?s[fac].lower_bound(dfn[u]):s[fac].upper_bound(dfn[u]));
if(it!=s[fac].begin()){
--it;
if(*it>=dfn[top[u]])return*it;
}
u=fa[top[u]];
}
return-1;
}
signed main(){
for(int i=2;i<V;++i){
if(!notp[i])pr[++cnt]=pre[i]=i;
for(int j=1;i*pr[j]<V;++j){
notp[i*pr[j]]=1,pre[i*pr[j]]=pr[j];
if(!(i%pr[j]))break;
}
}
read(n),read(m);
for(int i=1;i<=n;++i)read(a[i]);
for(int i=1,u,v;i<n;++i){
read(u),read(v);
g[u].emplace_back(v),g[v].emplace_back(u);
}
dfs1(1),dfs2(top[1]=1),dfs3(1);
for(int i=1,op,x,y;i<=m;++i){
read(op),read(x);
if(op==1){
int ret=-1;
for(int tmp=a[x];tmp!=1;tmp/=pre[tmp]){
if(!vis[pre[tmp]])vis[pre[tmp]]=1,ret=max(ret,query(x,pre[tmp]));
}
print(ret==-1?-1:id[ret]);
reVis(a[x]);
}else{
read(y);
for(int tmp=a[x];tmp!=1;tmp/=pre[tmp]){
if(!vis[pre[tmp]])vis[pre[tmp]]=1,s[pre[tmp]].erase(dfn[x]);
}
reVis(a[x]);
a[x]=y;
for(int tmp=y;tmp!=1;tmp/=pre[tmp]){
if(!vis[pre[tmp]])vis[pre[tmp]]=1,s[pre[tmp]].insert(dfn[x]);
}
reVis(y);
}
}
return 0;
}

浙公网安备 33010602011771号