[JZOJ6086]【GDOI2019模拟2019.3.26】动态半平面交【数据结构】

37 篇文章 0 订阅
25 篇文章 0 订阅

Descriptionn

在这里插入图片描述
强制在线
n , q ≤ 100000 , a ≤ 1 0 7 n,q\leq 100000,a\leq 10^7 n,q100000,a107

Solution

之前好像做过一道叫七彩树的题,是这题的弱化版。

先考虑lcm,它实际上是每一个质因子的出现的指数取最大值然后乘积。

这里我们有一个很妙的转化。

考虑一个数 a i a_i ai,它含有某一个因子 p k p^k pk,其中p为质数。
我们将 p p p的每一个指数看做一个颜色,也就是说这个点有 p 1 , p 2 . . . p k p^1,p^2...p^k p1,p2...pkk种颜色,这k种颜色的权值都是p。

那么问题就转化为求查询范围内的所有点的出现过的颜色的权值积

考虑题目要求的点的限制,实际上就是 d f s dfs dfs序在 [ d f n [ u ] , d f n [ u ] + s i z e [ u ] ) [dfn[u],dfn[u]+size[u]) [dfn[u],dfn[u]+size[u]),且深度小于 d e p [ u ] + d dep[u]+d dep[u]+d的所有点。

我们可以将所有点按照深度排序,将深度从小到大插入点,用一棵对于深度可持久化的线段树来维护DFS序,预先算好答案,这样就能在每个询问的时候直接在可持久化线段树上找了。

维护树上每一种颜色只算一次的和或者乘积有一个很经典的套路,我们将颜色相同的点拉出来,按照DFS序排序,在每个点打上+标记,在相邻点的lca处打上-标记。

加上深度限制也是类似的,我们用一个set来维护同种颜色的DFS序大小关系。
考虑加入一个颜色为c的点u的影响,它会修改它与它的前驱和后继的lca处的标记,直接在当前深度的线段树上改答案即可。

分析复杂度,至多有 n log ⁡ a n \log a nloga种颜色,每种颜色的插入和修改都是一个log的
时间复杂度 O ( n log ⁡ 2 n ) O(n\log^2 n) O(nlog2n)

Code

#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 100005
#define M 200005
#define LL long long
#define mo 998244353
using namespace std;

int pi[11*N],ap[N][10][2],fs[N],nt[2*N],dt[2*N],n,q,l,m1,fr[100*N],le[N],dep[N],dfn[N],sz[N],fa[20][N];
LL pr[N];
bool bz[100*N];
void link(int x,int y)
{
	nt[++m1]=fs[x];
	dt[fs[x]=m1]=y;
}

void prp()
{
	int r=1e7;
	fo(i,2,r)
	{
		if(!bz[i]) pi[++pi[0]]=i,fr[i]=i;
		for(int j=1;j<=pi[0]&&i*pi[j]<=r;j++)
		{
			bz[i*pi[j]]=1,fr[i*pi[j]]=pi[j];
			if(i%pi[j]==0) break;
		}
	}
}

void dfs(int k)
{
	dfn[k]=++dfn[0];
	sz[k]=1;
	dep[k]=dep[fa[0][k]]+1;
	for(int i=fs[k];i;i=nt[i])
	{
		int p=dt[i];
		if(p!=fa[0][k]) fa[0][p]=k,dfs(p),sz[k]+=sz[p];
	}
}

LL ksm(LL k,LL n)
{
	LL s=1;
	for(;n;n>>=1,k=k*k%mo) if(n&1) s=s*k%mo;
	return s;
}

int lca(int x,int y)
{
	if(dep[x]>dep[y]) swap(x,y);
	for(int j=dep[y]-dep[x],c=0;j;c++,j>>=1) if(j&1) y=fa[c][y];
	for(int j=19;x!=y;)
	{
		while(j&&fa[j][x]==fa[j][y]) j--;
		x=fa[j][x],y=fa[j][y];
	}
	return x;
}

int d[N];
bool cmp(int x,int y)
{
	return(dep[x]<dep[y]);
}
struct node
{
	int x,d;
	friend bool operator <(node x,node y){return x.d<y.d;}
};

set<node> bp[100*N];
typedef set<node>::iterator IT;

//segment tree
int rt[N],t[100*M][2],n1;
LL sv[100*M];

int opx,opy;
void nwp(int &x) {if(!x) x=++n1,sv[x]=1;}
void ins(int k,int l,int r)
{
	if(l==r) sv[k]=sv[k]*(LL)opy%mo;
	else
	{
		int mid=(l+r)>>1;
		if(opx<=mid) nwp(t[k][0]),ins(t[k][0],l,mid);
		else nwp(t[k][1]),ins(t[k][1],mid+1,r);
		sv[k]=sv[t[k][0]]*sv[t[k][1]]%mo;
	}
}
void merge(int &k,int x,int l,int r)
{
	if(!k) {k=x;return;}
	if(!x) return;
	if(l==r) {sv[k]=sv[k]*sv[x]%mo;return;}
	int mid=(l+r)>>1;
	merge(t[k][0],t[x][0],l,mid),merge(t[k][1],t[x][1],mid+1,r);
	sv[k]=sv[t[k][0]]*sv[t[k][1]]%mo;
}

LL query(int k,int l,int r)
{
	if(opx>opy||opx>r||opy<l) return 1;
	if(opx<=l&&r<=opy) return sv[k];
	int mid=(l+r)>>1;
	return query(t[k][0],l,mid)*query(t[k][1],mid+1,r)%mo;
}

int main()
{
	cin>>l>>n;
	fo(i,1,n) scanf("%lld",&pr[i]);
	fo(i,1,n-1)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		link(x,y),link(y,x);
	}
	prp();
	
	fo(i,1,n)
	{
		d[0]=0;
		int k=pr[i];
		while(k>1) d[++d[0]]=fr[k],k/=fr[k];
		sort(d+1,d+d[0]+1);
		fo(j,1,d[0]) 
		{
			if(j==1||d[j]!=d[j-1]) le[i]++,ap[i][le[i]][0]=d[j];
			ap[i][le[i]][1]++;
		}
	}

	dfs(1);

	fo(j,1,19) fo(i,1,n) fa[j][i]=fa[j-1][fa[j-1][i]];
	fo(i,1,n) d[i]=i;
	sort(d+1,d+n+1,cmp);
	sv[0]=1;
	for(int i=1,j=1;j<=n;i++)
	{
		rt[i]=++n1;
		while(j<=n&&dep[d[j]]<=i) 
		{	
			int k=d[j];
			fo(p1,1,le[k])
			{
				int p=ap[k][p1][0],v=p;
				LL np=ksm(p,mo-2);
				fo(q,1,ap[k][p1][1])
				{
					bp[v].insert((node){k,dfn[k]});
					IT lf=bp[v].begin(),ri=bp[v].end(),w=bp[v].find((node){k,dfn[k]});
					ri--;
					opx=dfn[k],opy=p,ins(rt[i],1,n);
					if((*w).x==(*lf).x)
					{
						if((*w).x!=(*ri).x) 
						{
							ri=w,ri++;
							opx=dfn[lca(k,(*ri).x)],opy=np,ins(rt[i],1,n);
						}
					}
					else  
					{
						lf=w,lf--;
						opx=dfn[lca(k,(*lf).x)],opy=np,ins(rt[i],1,n);
						if((*w).x!=(*ri).x)
						{
							ri=w,ri++;
							opx=dfn[lca(k,(*ri).x)],opy=np,ins(rt[i],1,n);
							opx=dfn[lca((*lf).x,(*ri).x)],opy=p,ins(rt[i],1,n);
						}
					}
					v*=p;
				}
			}
			j++;
		}
		merge(rt[i],rt[i-1],1,n);
	}
	cin>>q;

	LL ans=0;
	fo(i,1,q)
	{
		int x,u;
		scanf("%d%d",&x,&u);
		if(l) x^=ans,u^=ans;
		opx=dfn[x],opy=dfn[x]+sz[x]-1;
		printf("%lld\n",ans=query(rt[min(dep[d[n]],dep[x]+u)],1,n));
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值