Codeforces Round #264 (Div. 2) E. Caisa and Tree

E. Caisa and Tree

//最近工作繁忙,决定先不通刷cf套题,换刷难度系数2000以上的题

题意:给一颗以1为根的树,有q次操作,1 v表示求根到v权值与av不互质的深度最大的点。2 u w 表示把节点 u 的权值改为w,最多50次修改。

思路:我应该是第一个用主席树切这个题的吧....把每个节点的权值质因数分解,每颗线段树 rt[i] 存从根到 i 节点,每个质因数 x 节点存深度最大的点(该点权值包含x质因数),然后每次查询v,分解v的质因数,然后找v的父节点的主席树找这些质因数包含最大深度的节点就行。

#include<bits/stdc++.h>
using namespace std;
const int N=2e6,maxm=1e5+10;
int vis[2000001],pri[200010],cnt,a[maxm],f[maxm],dep[maxm],id[maxm*20];
vector<int>G[100001];
int rt[maxm],ls[maxm*20*7],rs[maxm*20*7],tree[maxm*20*7],sz;
void init()
{
	for(int i=2;i<=N;i++)
	if(!vis[i])
	{
		pri[++cnt]=i;
		id[i]=cnt;
		for(int j=i;j<=N;j+=i)
		vis[j]=1;
	}
}
void up(int pre,int &o,int l,int r,int k,int v)
{
	o=++sz;
	ls[o]=ls[pre];
	rs[o]=rs[pre];
	if(l==r)
	{
		tree[o]=v;
		return;
	} 
	int m=(l+r)/2;
	if(k<=m)up(ls[pre],ls[o],l,m,k,v);
	else up(rs[pre],rs[o],m+1,r,k,v);
}
int qu(int o,int l,int r,int k)
{
	if(l==r)
	return tree[o];
	int m=(l+r)/2;
	if(k<=m)return qu(ls[o],l,m,k);
	else return qu(rs[o],m+1,r,k);
}
void dfs(int u,int fa,int deep)
{
	dep[u]=deep;
	f[u]=fa;
	int k=1;
	int m=a[u];
	for(;k<=cnt&&pri[k]*pri[k]<=m;k++)
	if(m%pri[k]==0)
	{
		int x=pri[k];
		if(!rt[u])
		up(rt[fa],rt[u],1,200000,k,u);
		else
		up(rt[u],rt[u],1,200000,k,u);
		while(m%x==0&&m>=x)
		m/=x;
	}
	if(m!=1)
	{
		if(!rt[u])
		up(rt[fa],rt[u],1,200000,id[m],u);
		else
		up(rt[u],rt[u],1,200000,id[m],u);
	}
	if(!rt[u])rt[u]=rt[fa];
	for(int i=0;i<G[u].size();i++)
	if(G[u][i]!=fa)
	dfs(G[u][i],u,deep+1);
}
int main()
{
	init();
	int n,q,u,v,op;
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++)
	scanf("%d",&a[i]);
	for(int i=1;i<n;i++)
	{
		scanf("%d%d",&u,&v);
		G[u].push_back(v);
		G[v].push_back(u);
	}
	dfs(1,0,1);
	while(q--)
	{
		scanf("%d%d",&op,&u);
		if(op==1)
		{
			int ans=-1;
			v=f[u];
			int m=a[u],k=1;
			for(;k<=cnt&&pri[k]*pri[k]<=m;k++)
			if(m%pri[k]==0)
			{
				int x=pri[k];
				{
					int tmp=qu(rt[v],1,200000,k);
					if(ans==-1&&tmp)ans=tmp;
					if(tmp&&dep[ans]<dep[tmp])
					ans=tmp;
				}
				while(m%x==0&&m>=x)
				m/=x;
			}
			if(m!=1)
			{
				int tmp=qu(rt[v],1,200000,id[m]);
				if(ans==-1&&tmp)ans=tmp;
				if(tmp&&dep[ans]<dep[tmp])
				ans=tmp;				
			}
			printf("%d\n",ans);
		}
		else
		{
			scanf("%d",&a[u]);
			for(int i=1;i<=n;i++)
			rt[i]=0;
			for(int i=1;i<=sz;i++)
			ls[i]=rs[i]=tree[i]=0;
			sz=0;
			dfs(1,0,1);
		}
	}
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长沙橘子猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值