JZOJ 5930. 【NOIP2018模拟10.26】山花

63 篇文章 0 订阅
6 篇文章 0 订阅

Description

3.1 Background
春日的山中灌木茂盛,几乎长到了人的腰间,将山间都铺满了绿色。雨后的灌木之间还带着晨露,总会沾湿走过的行人的衣裳。
林中枝叶茂密,不过树木长的并不紧,遮不住天上,阳光落下照在山路上的灌木丛和落叶上。
山的另一侧,是漫山的花树,覆盖在山上,一直蔓延到山下,白瓣在微暖的阳光里透着粉红。风吹过,成片的花树摇动,花瓣翻飞而起,飘散开来,叫人移不开眼睛……
呵……这漫山的花树,叫小S怎能去雨露均沾呢……

3.2 Piece雨露均沾是不可能的,这辈子都不可能的,人力气又小,只能待到山花烂漫时,自取那满山烂漫来。3.3 Description今天又是去采花的好日子啊∼∼小S站在山顶,发现今日的花树们,在不甚平坦的山上,焕发出了别样的光彩。简单来说,它们组成了一棵以1为根的树(QuQ…每棵花树上有若干朵花,具体的,在编号为i的花树上有ai朵花。山花自然是越多越好,但小S却做不到雨露均沾…为了维护山间的生态多样性和自己的名誉和精力

小S决定从某一个节点u开始对其子树中与u距离小于K的节点代表的花树进行采摘。
特别的,节点u代表的花树也会被采摘。
依旧受限于精力,小S并不会亲自去采摘而是使用Extremely Strong的工具进行采摘。
我们定义一个工具的能力为c,小S会采摘的山树集合为T
那么小S能采摘到的山花数量fT = Πi∈T (ai
, c)
现在对于给定的树和阀值K,小S想要知道每一组询问的fT

Input

第一行,三个正整数n, Q, K,代表花树的棵数,询问次数和阀值。
接下来一行n个正整数,其中第i个数代表编号为i的花树的花的个数ai。
接下来n−1行,描述了花树们所形成的那棵树,每行两个正整数u, v,代表编号为u和v的花树直接相连。
接下来Q行,每行描述了一次询问,包含两个正整数x, c代表这次小S决定从编号为x的花树开始采摘,这次工具的能力为c。

Output

共Q行,每行一个整数ans,满足ans ≡ fi (mod 998244353)其中fi为第i次询问的答案,即能采摘到的山花数量

Sample Input

4 2 2
6 25 12 5
1 2
1 3
2 4
1 5
4 5

Sample Output

5
5

Data Constraint

Sample Inputx & Outputx
见选手下发文件中C_ex0.in/ans,C_ex1.in/ans。
Data Constraint

Solution

  • 显然我们可以对于每个质因子分开考虑。

  • 离线算出每个质因子对询问的贡献,那么乘起来就得到每个询问的答案了。

  • 因为: 2 ∗ 3 ∗ 5 ∗ 7 ∗ 11 ∗ 13 ∗ 17 ∗ 19 = 9699690 &lt; 1 0 7 2*3*5*7*11*13*17*19=9699690&lt;10^7 235711131719=9699690<107

  • 所以一个数不同的质因子个数最多为 8 8 8 ,近似于一个 l o g log log

  • 那么我们枚举一个质因子 p p p,考虑计算其贡献的答案。

  • 我们把含有 p p p a i a_i ai 和询问 c c c 都拿出来,按其中含有 p p p 的个数从小到大排序( a i = a ∗ p k a_i=a*p^k ai=apk)。

  • 从左到右扫,那么对于一个询问,它前面的 a i a_i ai 与自己做 gcd 时得到 k k k 的个数肯定小于自己。

  • 相反的,后面的 a i a_i ai 与自己的 gcd 得到 k k k 的个数肯定大于等于自己。

  • 于是我们算出前面 k k k 的和、加上后面 a i a_i ai 的个数乘上自己的 k k k 个数(记为 s u m sum sum),

  • 就得到了关于这个询问的贡献,即乘上 p s u m p^{sum} psum

  • 我们如何统计哪些 a i a_i ai 能被计算进询问呢?用dfs序呀!

  • x x x 点处打上+1标记,在 x x x K K K 级祖先处打上-1标记,用树状数组查询一段区间即可。

  • 开始时我们需要将每个 a i a_i ai 分解质因数,我们可以先线筛出 1 0 7 10^7 107 以内的质数。

  • 这里有个小技巧,筛的时候不是从 i i i 筛掉 i ∗ f [ j ] i*f[j] if[j] 吗?

  • 我们设一个数组 p r e pre pre ,使得 p r e [ i ∗ f [ j ] ] = f [ j ] pre[i*f[j]]=f[j] pre[if[j]]=f[j] ,那么我们顺着 p r e [ a i ] pre[a_i] pre[ai] 就能将 a i a_i ai 分解了。

  • 那么时间复杂度为 O ( n   l o g 2 n ) O(n\ log^2 n) O(n log2n)

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cctype>
using namespace std;
typedef long long LL;
const int N=1e5+5,M=1e7+5,mo=998244353;
struct data
{
	int x,y,z;
}b[N<<1],c[N],t;
int n,q,k,tot;
int first[N],nex[N<<1],en[N<<1];
int a[N],f[N*7],pre[M],g[N*7];
int ans[N],dfn[N],size[N],fa[N],st[N],hl[N],hr[N],pos[M];
int vis[M],val[M];
bool bz[M];
vector<data>ss[N*7];
inline int read()
{
    int X=0,w=0; char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
void write(int x)
{
	if(x>9) write(x/10);
	putchar(x%10+'0');
}
inline void insert(int x,int y)
{
	nex[++tot]=first[x];
	first[x]=tot;
	en[tot]=y;
}
void dfs(int x,int y,int z)
{
	dfn[x]=++tot;
	size[x]=1;
	st[z]=x;
	if(z>k) fa[x]=st[z-k];
	for(int i=first[x];i;i=nex[i])
		if(en[i]^y)
		{
			dfs(en[i],x,z+1);
			size[x]+=size[en[i]];
		}
}
inline bool cmp(data x,data y)
{
	return x.y<y.y;
}
inline void changel(int x,int y)
{
	while(x<=n) hl[x]+=y,x+=x&-x;
}
inline int findl(int x)
{
	int sum=0;
	while(x) sum+=hl[x],x-=x&-x;
	return sum;
}
inline void changer(int x,int y)
{
	while(x<=n) hr[x]+=y,x+=x&-x;
}
inline int findr(int x)
{
	int sum=0;
	while(x) sum+=hr[x],x-=x&-x;
	return sum;
}
inline int ksm(int x,int y)
{
	int s=1;
	while(y)
	{
		if(y&1) s=(LL)s*x%mo;
		x=(LL)x*x%mo;
		y>>=1;
	}
	return s;
}
int main()
{
	freopen("C.in","r",stdin);
	freopen("C.out","w",stdout);
	n=read(),q=read(),k=read();
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=1;i<n;i++)
	{
		int x=read(),y=read();
		insert(x,y);
		insert(y,x);
	}
	tot=0;
	dfs(1,0,1);
	for(int i=2;i<M;i++)
	{
		if(!bz[i]) f[++f[0]]=i;
		for(int j=1;j<=f[0] && i*f[j]<M;j++)
		{
			bz[i*f[j]]=true;
			pre[i*f[j]]=f[j];
			if(i%f[j]==0) break;
		}
	}
	for(int i=1;i<=q;i++)
	{
		c[i].x=read(),c[i].y=read(),c[i].z=i;
		ans[i]=1;
	}
	memset(bz,false,sizeof(bz));
	tot=0;
	for(int i=1;i<=n;i++)
	{
		int x=a[i];
		tot++;
		st[0]=0;
		while(pre[x])
		{
			if(vis[pre[x]]<tot)
			{
				vis[pre[x]]=tot;
				st[++st[0]]=pre[x];
				val[pre[x]]=0;
			}
			val[pre[x]]++;
			if(!bz[pre[x]])
			{
				bz[pre[x]]=true;
				g[++g[0]]=pre[x];
				pos[pre[x]]=g[0];
			}
			x/=pre[x];
		}
		if(vis[x]<tot)
		{
			vis[x]=tot;
			st[++st[0]]=x;
			val[x]=0;
		}
		val[x]++;
		if(!bz[x])
		{
			bz[x]=true;
			g[++g[0]]=x;
			pos[x]=g[0];
		}
		for(int j=1;j<=st[0];j++)
			ss[pos[st[j]]].push_back((data){i,val[st[j]],0});
	}
	for(int i=1;i<=q;i++)
	{
		int x=c[i].y;
		tot++;
		st[0]=0;
		while(pre[x])
		{
			if(vis[pre[x]]<tot)
			{
				vis[pre[x]]=tot;
				st[++st[0]]=pre[x];
				val[pre[x]]=0;
			}
			val[pre[x]]++;
			x/=pre[x];
		}
		if(vis[x]<tot)
		{
			vis[x]=tot;
			st[++st[0]]=x;
			val[x]=0;
		}
		val[x]++;
		for(int j=1;j<=st[0];j++) ss[pos[st[j]]].push_back((data){c[i].x,val[st[j]],i});
	}
	for(int p0=1;p0<=g[0];p0++)
	{
		int p=g[p0],cnt=tot=0;
		for(int i=0;i<(int)ss[p0].size();i++)
		{
			b[++tot]=ss[p0][i];
			if(b[tot].z) cnt++;
		}
		if(!cnt) continue;
		sort(b+1,b+1+tot,cmp);
		for(int i=1;i<=tot;i++)
			if(!b[i].z)
			{
				changer(dfn[b[i].x],1);
				if(fa[b[i].x]) changer(dfn[fa[b[i].x]],-1);
			}
		int sum1=0,sum2=0;
		for(int i=1;i<=tot;i++)
			if(!b[i].z)
			{
				changel(dfn[b[i].x],b[i].y);
				changer(dfn[b[i].x],-1);
				if(fa[b[i].x])
				{
					changel(dfn[fa[b[i].x]],-b[i].y);
					changer(dfn[fa[b[i].x]],1);
				}
			}else
			{
				int sum1=findl(dfn[b[i].x]+size[b[i].x]-1)-findl(dfn[b[i].x]-1);
				int sum2=findr(dfn[b[i].x]+size[b[i].x]-1)-findr(dfn[b[i].x]-1);
				int sum=(sum1+(LL)sum2*b[i].y)%mo;
				ans[b[i].z]=(LL)ans[b[i].z]*ksm(p,sum)%mo;
			}
		for(int i=1;i<=tot;i++)
			if(!b[i].z)
			{
				changel(dfn[b[i].x],-b[i].y);
				if(fa[b[i].x]) changel(dfn[fa[b[i].x]],b[i].y);
			}
	}
	for(int i=1;i<=q;i++) write(ans[i]),putchar('\n');
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值