P16829 题解
_buzhidao_
·
·
题解
本文中认为 $n,q,V$ 同阶。
首先树是假的,直接拍到序列上。
考虑经典性质:一个数至多有一个大于自己的平方根的质因子。
考虑分治,对于小于 $\sqrt n$ 的质因子,直接用势能线段树做就可以了。这部分别的题解讲的还是比较清楚的。这部分的时间复杂度是 $O(n\sqrt n)$ 的。
对于大于 $\sqrt n$ 的质因子,问题等价于维护一个序列,实现以下操作:
* 给定 $v$,将某个区间中所有不是 $v$ 的数变成 $0$。
* 查询某个区间中的所有数去重后的乘积。
考虑分块,取块长 $S=n^\frac23$。
对于所有**连续的块形成的区间**,用 `bitset` 维护这个区间中出现的数和这些数的乘积。这样的区间有 $O((n^\frac13)^2)=O(n^\frac23)$ 个。
利用**每个数只会被变成零一次**这个性质,容易实现以下操作:
* 找到某个修改操作中,应该被变成零的数。
* 将一个数变成零,并暴力修改所有受影响的信息。
* 查询某个区间中的所有数去重后的乘积。
于是我们就愉快地在 $O(n^\frac53)$ 的时间内解决了这个问题。
:::success[代码]
比较 dirty work。
```cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
constexpr ll mod=998244353;
constexpr int V=1e5;
bool npri[100005];
vector<int> smallpri,bigpri;
int toid[100005];
void init(){
npri[0]=npri[1]=1;
for(int i=2;i<=V/i;++i){
if(npri[i]) continue;
for(int j=i*i;j<=V;j+=i) npri[j]=1;
}
for(int i=2;i<=V;++i){
if(npri[i]) continue;
if(i<=V/i) toid[i]=smallpri.size(),smallpri.push_back(i);
else toid[i]=bigpri.size(),bigpri.push_back(i);
}
}
using num=vector<pair<int,int>>;
num cal(int x){
num res;
for(int p:smallpri){
if(x%p) continue;
res.push_back({p,0});
while(x%p==0) ++res.back().second,x/=p;
}
if(x>1) res.push_back({x,1});
return res;
}
ll inv[100005];
int n,q;
num s[100005];
int a[100005];
vector<int> t[100005];
int dfn[100005],dfncnt;
int tout[100005];
void dfs(int u,int fa){
dfn[u]=++dfncnt;
for(int v:t[u]) if(v!=fa) dfs(v,u);
tout[u]=dfncnt;
}
using value=pair<int,int>;
struct node{
int l,r;
value val;
node() = default;
node(int l,int r):l(l),r(r){}
};
#define lsp (p<<1)
#define rsp (p<<1|1)
class sgt{
private:
array<node,400005> t;
void pushup(int p);
public:
sgt() = default;
void buildt(int p,int l,int r);
void upd(int p,int u,int v);
value query(int p,int l,int r);
void chkmin(int l,int r,int v);
};
void sgt::pushup(int p){
t[p].val=max(t[lsp].val,t[rsp].val);
}
void sgt::buildt(int p,int l,int r){
t[p]={l,r};
if(l==r) return;
int mid=(l+r)>>1;
buildt(lsp,l,mid);
buildt(rsp,mid+1,r);
}
void sgt::upd(int p,int u,int v){
if(t[p].l==t[p].r) return (void)(t[p].val={v,u});
int mid=(t[p].l+t[p].r)>>1;
if(u<=mid) upd(lsp,u,v);
else upd(rsp,u,v);
pushup(p);
}
value sgt::query(int p,int l,int r){
if(l<=t[p].l&&t[p].r<=r) return t[p].val;
int mid=(t[p].l+t[p].r)>>1;
value res={0,0};
if(l<=mid) res=query(lsp,l,r);
if(r>mid) res=max(res,query(rsp,l,r));
return res;
}
void sgt::chkmin(int l,int r,int v){
value val=query(1,l,r);
if(val.first<=v) return;
upd(1,val.second,v);
chkmin(l,r,v);
}
sgt small[65];
constexpr int S=2000;
int o[100005];
int bi[100005];
int L[55],R[55];
bitset<9527> big[55][55];
ll pi[55][55];
int mark[55];
set<int> app[9527];
void remove(int i){
int p=o[i];
o[i]=0;
auto& app=::app[toid[p]];
app.erase(i);
if(app.empty()){
for(int b=1;b<=bi[n];++b){
for(int c=b;c<=bi[n];++c){
if(big[b][c][toid[p]]) pi[b][c]=pi[b][c]*inv[p]%mod;
big[b][c][toid[p]]=0;
}
}
return;
}
auto it=app.upper_bound(i);
int l,r;
if(it==app.begin()) l=0,r=*it;
else if(it==app.end()) l=*(--it),r=n+1;
else r=*it,l=*(--it);
int lb=bi[l]+1,rb=bi[r]-1;
for(int b=lb;b<=rb;++b){
for(int c=b;c<=rb;++c){
if(big[b][c][toid[p]]) pi[b][c]=pi[b][c]*inv[p]%mod;
big[b][c][toid[p]]=0;
}
}
}
void clear(int l,int r,int p){
if(bi[l]==bi[r]){
for(int i=l;i<=r;++i) if(o[i]&&o[i]!=p) remove(i);
return;
}
for(int i=l;i<=R[bi[l]];++i) if(o[i]&&o[i]!=p) remove(i);
for(int i=L[bi[r]];i<=r;++i) if(o[i]&&o[i]!=p) remove(i);
for(int b=bi[l]+1;b<=bi[r]-1;++b){
if(!mark[b]){
for(int i=L[b];i<=R[b];++i) if(o[i]&&o[i]!=p) remove(i);
mark[b]=p;
}
else if(mark[b]!=-1&&mark[b]!=p){
for(int i=L[b];i<=R[b];++i) if(o[i]&&o[i]!=p) remove(i);
mark[b]=-1;
}
}
}
bool vis[9527];
int main(){
ios::sync_with_stdio(0);
init();
inv[1]=1;
for(int i=2;i<=1e5;++i) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
cin>>n>>q;
for(int u=1;u<=n;++u) cin>>a[u];
for(int i=1;i<n;++i){
int u,v;
cin>>u>>v;
t[u].push_back(v),t[v].push_back(u);
}
dfs(1,0);
for(int u=1;u<=n;++u) s[dfn[u]]=cal(a[u]);
for(int i=0;i<65;++i) small[i].buildt(1,1,n);
for(int i=1;i<=n;++i){
bi[i]=(i-1)/S+1;
if(bi[i]!=bi[i-1]) L[bi[i]]=i;
R[bi[i]]=i;
}
bi[n+1]=bi[n]+1;
for(int i=1;i<=n;++i){
for(auto [p,c]:s[i]){
if(p<=smallpri.back()) small[toid[p]].upd(1,i,c);
else app[toid[p]].insert(i),big[bi[i]][bi[i]][toid[p]]=1,o[i]=p;
}
}
for(int b=1;b<=bi[n];++b){
for(int c=b+1;c<=bi[n];++c){
big[b][c]=big[b][c-1]|big[c][c];
}
}
for(int b=1;b<=bi[n];++b){
for(int c=b;c<=bi[n];++c){
pi[b][c]=1;
for(int i=0;i<9527;++i) if(big[b][c][i]) pi[b][c]=pi[b][c]*bigpri[i]%mod;
}
}
while(q--){
int op;
cin>>op;
if(op==1){
int u,x;
cin>>u>>x;
int i=0;
bool fl=1;
for(auto [p,c]:cal(x)){
if(p<=smallpri.back()){
while(i<toid[p]) small[i++].chkmin(dfn[u],tout[u],0);
small[toid[p]].chkmin(dfn[u],tout[u],c);
i=toid[p]+1;
}
else clear(dfn[u],tout[u],p),fl=0;
}
while(i<65) small[i++].chkmin(dfn[u],tout[u],0);
if(fl) clear(dfn[u],tout[u],bigpri[0]),clear(dfn[u],tout[u],bigpri[1]);
}
else{
int u;
cin>>u;
int l=dfn[u],r=tout[u];
ll ans=1;
for(int i=0;i<65;++i){
int c=small[i].query(1,l,r).first;
while(c--) ans=ans*smallpri[i]%mod;
}
if(bi[r]-bi[l]<=1){
for(int i=l;i<=r;++i) if(o[i]&&!vis[toid[o[i]]]) ans=ans*o[i]%mod,vis[toid[o[i]]]=1;
for(int i=l;i<=r;++i) if(o[i]) vis[toid[o[i]]]=0;
}
else{
auto& bs=big[bi[l]+1][bi[r]-1];
ans=ans*pi[bi[l]+1][bi[r]-1]%mod;
for(int i=l;i<=R[bi[l]];++i) if(o[i]&&!vis[toid[o[i]]]&&!bs[toid[o[i]]]) ans=ans*o[i]%mod,vis[toid[o[i]]]=1;
for(int i=L[bi[r]];i<=r;++i) if(o[i]&&!vis[toid[o[i]]]&&!bs[toid[o[i]]]) ans=ans*o[i]%mod,vis[toid[o[i]]]=1;
for(int i=l;i<=R[bi[l]];++i) if(o[i]) vis[toid[o[i]]]=0;
for(int i=L[bi[r]];i<=r;++i) if(o[i]) vis[toid[o[i]]]=0;
}
cout<<ans<<endl;
}
}
return 0;
}
```
:::