我很喜欢这道题。
题目大意:
给出一棵带点权树。对每个询问$ u,v,x $,求$prod_{i in P(u,v)}gcd(ai,x)$。其中$ P(u,v) $表示$ u $到$ v $的路径。
题目分析:
注意到权值大小不会超过$ 10^7 $,这似乎是在提示我们进行线性筛和质因数分解。我们就按照这个想法做。
其实我们分解完了之后我们会发现每个质数对于答案的影响是独立的,所以我们可以建素数个数个虚树。点数是多少呢?
对于每个点,它的点权$ai$只会分解出$O(log{ai})$种不同的素数。所以总共会分解出$O(nlog{a})$个点,这个上界是松的。
观察目标式,它是有这样的关系的:
$prod_{i in P(u,v)}gcd(a_i,x) = prod_{i in P(1,u)}gcd(a_i,x)*prod_{i in P(1,v)}gcd(a_i,x)*(prod_{i in P(1,lca(u,v))}gcd(a_i,x))^{-1}*(prod_{i in P(1,lca(u,v))}gcd(a_i,x))^{-1}*gcd(a_{lca},x) $
所以我们要做的仅仅是维护一个根节点到某个点$u$的答案。
考虑到我们上面已经分析出来每个素数是独立的,所以对于一个$x$,我们分别考虑它的每一个素因子$p$。一个质因子$p$和另一个质因子$q$的最大公约数一定是$1$,所以我们只考虑$x$的$p$和$a$的$p$的关系。
将$p$放进它对应的虚树中,我们查询的其实是${p^{sum_{i in P(1,h)}min(p_{ai},p_x)}}$(这一步变换可能太急了,这里用了一个初中知识点),其中$h$是查询点,$p_{ai}$是$ai$的$p$因子个数,$p_x同理$。没有在这个虚树中出现的路径上的点一定没有$p$这个因子,所以不用考虑。
对于上面那个式子,如果$p_{ai} leq p_x$,起作用的就是$p_{ai}$,否则是$p_x$。这意味着我们需要在一个很快的时间内求出从$h$到根的虚树路径中有多少点的点权小于$p_x$,同时维护它们的和。这是一个简单的事情,大家都知道主席树可以做这个做到很快。但主席树必要吗?
从空间渐进意义上来讲,主席树是必要的,因为如果直接开空间会爆,但是如果空间开到了一个比较大的值或者我的上界分析得太松了,那么主席树是不必要的。原因在于对于每个素数$p$对应着一个原始的数据$ai$,使得$p^{p_{ai}} leq ai$。所以$p_{ai}$是$O(loga)$级别的。所以虚树上的一次询问可以在$O(loga)$得到答复,虚数上每个点也只用开一个$ O(loga) $级别的桶存储。这样来说空间复杂度和时间复杂度是$ O(nlogxloga) $的。这里我们认为$O(n)=O(query)$。但是出于保险起见(换句话说,我无法证明这个上界有那么松),我们仍然采用主席树。
如果采用主席树,那么大家都晓得是从父亲节点那里作为历史状态。上面的分析告诉我们主席树只用开$O(loga)$作为长度。所以对于每个点,我们只会新增$ O(logloga) $个主席树的点。一次查询时间复杂度与空间相同,所以我们的空间复杂度是$ O(nlogxlogloga) $的。这里我们认为$O(n)=O(query)$。但是时间却不是和空间相同的,原因在于我们需要在每次结束后进行快速幂。我想了很久也没有想到如何克服快速幂的问题,所以时间与第一种做法相同,是$ O(qlogxloga) $的,因此在空间充裕的时候建议使用第一种做法。
upd:我阅读了题解,发现题解提供了一种空间更小的做法。就是对于每个质数建虚树然后在dfs的时候用树状数组维护一下即可。
upd2:我研究了一下codeforces,我认为它是这样测程序的。 首先采用不加优化跑,若TLE,则返回TLE,否则开O2重跑,返回运行时间。
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 #define RI register int 5 6 const int maxm = 10000020; 7 const int N = 10000000; 8 const int maxn = 100000+5; 9 const int Primenum = 780000; 10 const int mod = 1000000007; 11 12 int prime[Primenum],flag[maxm],minn[maxm],num; 13 14 int n,q,a[maxn]; 15 vector<int> g[maxn]; 16 vector<pair<int,int> > Control[Primenum],Fin[Primenum]; 17 18 int RMQ[maxn<<1][19],euler[maxn<<1],nE,app[maxn][2]; 19 20 int dep[maxn],fa[maxn],dfsin[maxn],dfsnum; 21 22 vector <int> Vdfs[Primenum],Gotin[Primenum]; 23 24 struct Ask{int u,v,x;}AQ[maxn]; 25 struct node{int len,tot,ch[2];}CMT[4000000]; 26 int numCMT; 27 28 int Vfa[1500000],Vfuck[1500000],vnum; 29 vector <int> Vds[Primenum]; 30 31 inline void fast_in(int &x){ 32 x = 0; char ch = getchar(); 33 while(ch > '9' || ch < '0') ch = getchar(); 34 while(ch <= '9' && ch >= '0') x = x*10+ch-'0',ch=getchar(); 35 } 36 37 inline int fast_pow(int now,int pw){ 38 int ans = 1,A = now,bit = 1; 39 while(bit <= pw){ 40 if(bit & pw) ans = (1ll*ans*A)%mod; 41 A = (1ll*A*A)%mod; 42 bit<<=1; 43 } 44 return ans; 45 } 46 47 inline void divide(int now){ 48 int last = minn[a[now]],Numlast = 0; 49 int p = a[now]; 50 while(p != 1){ 51 if(minn[p] == last) Numlast++; 52 else{ 53 Control[flag[last]].push_back(make_pair(now,Numlast)); 54 last = minn[p];Numlast = 1; 55 } 56 p /= minn[p]; 57 } 58 Control[flag[last]].push_back(make_pair(now,Numlast)); 59 } 60 61 void dfs(int now,int f,int dp){ 62 dep[now] = dp; fa[now] =f;dfsin[now] = ++num; 63 euler[++nE] = now;app[now][0] = app[now][1] = nE; 64 divide(now); 65 for(RI i=0;i<g[now].size();++i){ 66 if(g[now][i] == f) continue; 67 dfs(g[now][i],now,dp+1); 68 euler[++nE] = now; app[now][1] = nE; 69 } 70 } 71 72 inline int QueryLCA(int u,int v){ 73 int minn = min(app[u][0],app[v][0]),maxx = max(app[u][1],app[v][1]); 74 int k = 0; while((1<<k+1) <= (maxx-minn+1)) k++; 75 if(dep[RMQ[minn][k]] < dep[RMQ[maxx-(1<<k)+1][k]]){ 76 return RMQ[minn][k]; 77 }else return RMQ[maxx-(1<<k)+1][k]; 78 } 79 80 inline void BuildRMQ(){ 81 for(RI i=1;i<=nE;++i) RMQ[i][0] = euler[i]; 82 for(RI k=1;(1<<k)<=nE;k++) 83 for(RI i=1;i<=nE;++i){ 84 if(i+(1<<k) > nE) RMQ[i][k] = RMQ[i][k-1]; 85 else{ 86 if(dep[RMQ[i][k-1]] < dep[RMQ[i+(1<<k-1)][k-1]]) 87 RMQ[i][k] = RMQ[i][k-1]; 88 else RMQ[i][k] = RMQ[i+(1<<k-1)][k-1]; 89 } 90 } 91 } 92 93 inline void init(){ 94 flag[1] = 1; minn[1] = 1; 95 for(RI i=1;i<=N;++i){ 96 if(!flag[i]){prime[++num]=i;flag[i]=num;minn[i]=i;} 97 for(RI j=1;j<=num&&i*prime[j]<=N;++j){ 98 flag[i*prime[j]] = 1; 99 minn[i*prime[j]] = prime[j]; 100 if(i%prime[j] == 0) break; 101 } 102 } 103 dfs(1,0,1); 104 BuildRMQ(); 105 } 106 107 inline void read(){ 108 fast_in(n); 109 for(RI i=1;i<n;++i){ 110 int u,v;fast_in(u),fast_in(v); 111 g[u].push_back(v); g[v].push_back(u); 112 } 113 for(RI i=1;i<=n;++i) fast_in(a[i]); 114 fast_in(q); 115 } 116 117 int cmp(pair<int,int> alpha,pair<int,int> beta){ 118 if(dfsin[alpha.first] < dfsin[beta.first]) return 1; 119 else return 0; 120 } 121 122 inline int divisor(int now,int prm){ 123 int len = 0; 124 while(now%prm==0)len++,now/=prm; 125 return len; 126 } 127 128 inline void AddPoint(int last,int now,int tl,int tr,int data){ 129 if(tl == tr){ 130 if(data) CMT[now].len = CMT[last].len+1; 131 else CMT[now].len = CMT[last].len; 132 CMT[now].tot = CMT[last].tot+data; 133 return; 134 } 135 int mid = (tl+tr)/2; 136 if(data <= mid){ 137 CMT[now].ch[1] = CMT[last].ch[1]; CMT[now].ch[0] = ++numCMT; 138 AddPoint(CMT[last].ch[0],CMT[now].ch[0],tl,mid,data); 139 if(data) CMT[now].len = CMT[last].len+1; 140 else CMT[now].len = CMT[last].len; 141 CMT[now].tot = CMT[last].tot+data; 142 }else{ 143 CMT[now].ch[0] = CMT[last].ch[0]; CMT[now].ch[1] = ++numCMT; 144 AddPoint(CMT[last].ch[1],CMT[now].ch[1],mid+1,tr,data); 145 if(data) CMT[now].len = CMT[last].len+1; 146 else CMT[now].len = CMT[last].len; 147 CMT[now].tot = CMT[last].tot+data; 148 } 149 } 150 151 int sta[maxn],tp=0; 152 153 inline void BuildVirtualTree(){ 154 for(RI i=1;i<=num;++i){ 155 sort(Control[i].begin(),Control[i].end(),cmp); 156 int SIZE = Control[i].size(); 157 if(SIZE) Fin[i].push_back(Control[i][0]); 158 for(RI j=1;j<SIZE;++j){ 159 Fin[i].push_back(Control[i][j]); 160 int ff = QueryLCA(Control[i][j].first,Control[i][j-1].first); 161 Fin[i].push_back(make_pair(ff,divisor(a[ff],prime[i]))); 162 } 163 sort(Fin[i].begin(),Fin[i].end(),cmp); 164 SIZE = unique(Fin[i].begin(),Fin[i].end())-Fin[i].begin(); 165 while(Fin[i].size() != SIZE) Fin[i].pop_back(); 166 for(RI j=0;j<SIZE;++j) Vds[i].push_back(++vnum),Vdfs[i].push_back(dfsin[Fin[i][j].first]); 167 for(RI j=0;j<SIZE;++j) Gotin[i].push_back(0); 168 for(RI j=0;j<SIZE;++j){ 169 int jpts = Fin[i][j].first; 170 while(tp&&QueryLCA(Fin[i][sta[tp]].first,jpts)!=Fin[i][sta[tp]].first)tp--; 171 if(tp) Vfuck[Vds[i][j]] = sta[tp],Vfa[Vds[i][j]] = Vds[i][sta[tp]]; 172 sta[++tp]=j; 173 } 174 tp = 0; 175 for(RI j=0;j<SIZE;++j) { 176 if(Gotin[i][j]) continue; 177 int now = Vds[i][j]; 178 if(Fin[i][j].second == 0) Gotin[i][j] = Gotin[i][Vfuck[now]]; 179 else{ 180 Gotin[i][j] = ++numCMT; 181 AddPoint(Gotin[i][Vfuck[now]],Gotin[i][j],1,23,Fin[i][j].second); 182 } 183 } 184 } 185 } 186 187 int QueryonTree(int now,int tl,int tr,int tag){ 188 if(tl >= tag) return CMT[now].len*tag; 189 if(tr <= tag) return CMT[now].tot; 190 int mid = (tl+tr)/2; 191 return QueryonTree(CMT[now].ch[0],tl,mid,tag)+QueryonTree(CMT[now].ch[1],mid+1,tr,tag); 192 } 193 194 inline int Query(int now,int im,int pts){ 195 int TreeNum = flag[now]; 196 int p=lower_bound(Vdfs[TreeNum].begin(),Vdfs[TreeNum].end(),dfsin[pts])-Vdfs[TreeNum].begin(); 197 return fast_pow(now,QueryonTree(Gotin[TreeNum][p],1,23,im)); // 23 is const num 5E6 < 2^23 < 1E7 198 } 199 200 inline void work(){ 201 //puts("b"); 202 for(RI i=1;i<=q;++i){ 203 fast_in(AQ[i].u);fast_in(AQ[i].v);fast_in(AQ[i].x); 204 int u = AQ[i].u,v=AQ[i].v,x=AQ[i].x; 205 int lca = QueryLCA(u,v),last = minn[x],lastNum = 0,ans = 1; 206 while(x!=1){ 207 if(last == minn[x]) lastNum++; 208 else{ 209 if(a[u]%last!=0)Control[flag[last]].push_back(make_pair(u,0)); 210 if(a[v]%last!=0)Control[flag[last]].push_back(make_pair(v,0)); 211 last = minn[x],lastNum = 1; 212 } 213 x/=minn[x]; 214 } 215 if(a[u]%last!=0)Control[flag[last]].push_back(make_pair(u,0)); 216 if(a[v]%last!=0)Control[flag[last]].push_back(make_pair(v,0)); 217 } 218 //puts("h"); 219 BuildVirtualTree(); 220 for(RI i=1;i<=q;++i){ 221 int u = AQ[i].u,v=AQ[i].v,x=AQ[i].x; 222 int lca = QueryLCA(u,v),last = minn[x],lastNum = 0,ans = 1; 223 while(x!=1){ 224 if(last == minn[x]) lastNum++; 225 else{ 226 ans = (1ll*ans*Query(last,lastNum,u)) % mod; 227 ans = (1ll*ans*Query(last,lastNum,v)) % mod; 228 int zz = fast_pow(Query(last,lastNum,lca),mod-2);zz = (1ll*zz*zz)%mod; 229 ans = (1ll*ans*zz)%mod; 230 last = minn[x],lastNum = 1; 231 } 232 x/=minn[x]; 233 } 234 ans = (1ll*ans*Query(last,lastNum,u)) % mod; 235 ans = (1ll*ans*Query(last,lastNum,v)) % mod; 236 int zz = fast_pow(Query(last,lastNum,lca),mod-2);zz = (1ll*zz*zz)%mod; 237 ans = (1ll*ans*zz)%mod; 238 ans = (1ll*ans*__gcd(a[lca],AQ[i].x))%mod; 239 printf("%d ",ans); 240 } 241 } 242 243 int main(){ 244 read(); 245 init(); 246 work(); 247 return 0; 248 }