jzoj6086 动态半平面交 可持久化线段树+set

25 篇文章 0 订阅

Description


给定一棵n个节点带点权的树,要求资瓷q次询问
格式为(x,d),即求出x子树内距离x不超过d的点的点权的lcm,对998244353取模
n ≤ 1 0 5 ,    a i ≤ 1 0 7 n\le10^5,\; a_i\le10^7 n105,ai107

Solution


之前做过序列上的离线做法,上树就有点懵了
很常见的套路吧,没想到有点可惜

先不管那个深度的条件
我们把一个数分解,对于一个质因子p,将 p 1 、 p 2 、 p 3 . . . p^1、p^2、p^3... p1p2p3...的权值设为p,相同的数字我们只算一次贡献,那么就能解决lcm的问题了
然后我们对于相同的数字维护一个资瓷插入的虚树,每次讨论一下相邻节点的lca除掉贡献就能做到单点修改区间查询了,这样只需要一棵线段树就能做

考虑加上深度的限制会发生什么变化,那么就是深度<=某个数的节点才会被统计,那么我们按照深度建可持久化线段树就可以了

unorderd_map好像会快很多啊,不知道比赛能不能用呢

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <unordered_map>
#include <set>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)

typedef long long LL;
const int MOD=998244353;
const int N=100005;

struct edge {int y,next;} e[N*2];

std:: unordered_map <int,int> map;
std:: set <int>:: iterator it;
std:: set <int> set[N*30];

int size[N],pos[N],dfn[N],dep[N],fa[N],bl[N];
int rec[21][N*2],lg[N*2],in[N];
int p[N*7],rt[N],id[N],mn[N*100],a[N];
int L[N*600],R[N*600],P[N*600];
int ls[N],edCnt,tot,chp;

bool np[N*100];

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

void add_edge(int x,int y) {
	e[++edCnt]=(edge) {y,ls[x]}; ls[x]=edCnt;
	e[++edCnt]=(edge) {x,ls[y]}; ls[y]=edCnt;
}

void pre(int n) {
	rep(i,2,n) {
		if (!np[i]) p[++p[0]]=i,mn[i]=i;
		for (int j=1;1LL*i*p[j]<=n&&j<=p[0];++j) {
			np[i*p[j]]=1; mn[i*p[j]]=p[j];
			if (i%p[j]==0) break;
		}
	}
}

LL ksm(LL x,LL dep) {
	LL res=1;
	for (;dep;dep>>=1) {
		(dep&1)?(res=res*x%MOD):0;
		x=x*x%MOD;
	}
	return res;
}

void dfs(int x) {
	rec[0][++chp]=x; in[x]=chp;
	pos[x]=++pos[0]; dfn[pos[x]]=x;
	size[x]=1;
	for (int i=ls[x];i;i=e[i].next) {
		if (e[i].y==fa[x]) continue;
		dep[e[i].y]=dep[x]+1; fa[e[i].y]=x;
		dfs(e[i].y); size[x]+=size[e[i].y];
		rec[0][++chp]=x;
	}
}

void modify(int &now,int pre,int tl,int tr,int x,LL v) {
	now=++tot; L[tot]=L[pre],R[tot]=R[pre],P[tot]=P[pre];
	P[now]=v*P[now]%MOD;
	if (tl==tr) return ;
	int mid=(tl+tr)>>1;
	if (x<=mid) modify(L[now],L[pre],tl,mid,x,v);
	else modify(R[now],R[pre],mid+1,tr,x,v);
}

LL query(int now,int tl,int tr,int l,int r) {
	if (r<l||!now) return 1;
	if (tl>=l&&tr<=r) return P[now];
	int mid=(tl+tr)>>1; LL res=1;
	if (l<=mid) res=res*query(L[now],tl,mid,l,r);
	if (mid+1<=r) res=res*query(R[now],mid+1,tr,l,r);
	return (res>=MOD)?(res%MOD):res;
}

int get_lca(int x,int y) {
	x=in[x],y=in[y];
	if (x>y) std:: swap(x,y);
	int u=lg[y-x+1];
	x=rec[u][x],y=rec[u][y-(1<<u)+1];
	return dep[x]<dep[y]?x:y;
}

bool cmp(int x,int y) {
	return dep[x]<dep[y];
}

int main(void) {
	freopen("data.in","r",stdin);
	freopen("myp.out","w",stdout);
	pre(1e7); P[0]=1;
	int opt=read(),n=read(),lxf=0;
	rep(i,1,n) a[i]=read(),id[i]=i;
	rep(i,2,n) add_edge(read(),read());
	dfs(dep[1]=1);
	rep(i,2,chp) lg[i]=lg[i>>1]+1;
	rep(j,1,lg[chp]) {
		rep(i,1,chp-(1<<j)+1) {
			if (dep[rec[j-1][i]]>dep[rec[j-1][i+(1<<j-1)]]) {
				rec[j][i]=rec[j-1][i+(1<<j-1)];
			} else rec[j][i]=rec[j-1][i];
		}
	}
	std:: sort(id+1,id+n+1,cmp);
	rep(i,1,n) {
		int x=id[i],d=dep[x];
		rt[d]=rt[dep[id[i-1]]];
		for (;a[x]>1;) {
			int yqw=mn[a[x]],wjp=1;
			LL inv=ksm(mn[a[x]],MOD-2);
			while (a[x]%yqw==0&&(a[x]/=yqw)) {
				wjp=wjp*yqw;
				if (!map[wjp]) map[wjp]=++lxf;
				int now=map[wjp],tx=0,ty=0;
				set[now].insert(pos[x]);
				it=set[now].find(pos[x]);
				it++; if (it!=set[now].end()) tx=dfn[*it]; it--;
				if (it!=set[now].begin()) it--,ty=dfn[*it],it++;
				modify(rt[d],rt[d],1,n,pos[x],yqw);
				if (tx) modify(rt[d],rt[d],1,n,pos[get_lca(x,tx)],inv);
				if (ty) modify(rt[d],rt[d],1,n,pos[get_lca(x,ty)],inv);
				if (tx&&ty) modify(rt[d],rt[d],1,n,pos[get_lca(tx,ty)],yqw);
			}
		}
	}
	rep(i,dep[id[n]]+1,n) rt[i]=rt[i-1];
	LL lastans=0;
	for (int T=read(),x,d;T--;) {
		x=read()^(opt*lastans),d=read()^(opt*lastans);
		d=std:: min(dep[x]+d,n);
		printf("%lld\n", lastans=query(rt[d],1,n,pos[x],pos[x]+size[x]-1));
	}
	//while (1) int ggg=0;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值