Codeforces986E Prince's Problem 虚树可持久化线段树树状数组
Posted Menhera
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces986E Prince's Problem 虚树可持久化线段树树状数组相关的知识,希望对你有一定的参考价值。
我很喜欢这道题。
题目大意:
给出一棵带点权树。对每个询问$ 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(ai,x) = \prod_{i \in P(1,u)}gcd(ai,x)*\prod_{i \in P(1,v)}gcd(ai,x)*(\prod_{i \in P(1,lca(u,v))}gcd(ai,x))^{-1}*(\prod_{i \in P(1,fa[lca(u,v)])}gcd(ai,x))^{-1} $
所以我们要做的仅仅是维护一个根节点到某个点$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的时候用树状数组维护一下即可。
代码:
1 #include<bits/stdc++.h> 2 #pragma GCC optimize(2) 3 using namespace std; 4 5 #define RI register int 6 7 const int maxm = 10000020; 8 const int N = 10000000; 9 const int maxn = 102000; 10 const int Primenum = 800000; 11 const int mod = 1000000007; 12 13 int prime[Primenum],flag[maxm],minn[maxm],num; 14 15 int n,q,a[maxn]; 16 vector<int> g[maxn]; 17 vector<pair<int,int> > Control[Primenum],Fin[Primenum]; 18 19 int RMQ[maxn<<1][19],euler[maxn<<1],nE,app[maxn][2]; 20 21 int dep[maxn],fa[maxn],dfsin[maxn],dfsnum; 22 23 vector <int> Vdfs[Primenum],Gotin[Primenum]; 24 25 struct Ask{int u,v,x;}AQ[maxn]; 26 struct node{int len,tot,ch[2];}CMT[8000000]; 27 int numCMT; 28 29 int Vfa[1900000],Vfuck[1900000],vnum; 30 vector <int> Vds[Primenum]; 31 32 void fast_in(int &x){ 33 x = 0; char ch = getchar(); 34 while(ch > ‘9‘ || ch < ‘0‘) ch = getchar(); 35 while(ch <= ‘9‘ && ch >= ‘0‘) x = x*10+ch-‘0‘,ch=getchar(); 36 } 37 38 int fast_pow(int now,int pw){ 39 int ans = 1,A = now,bit = 1; 40 while(bit <= pw){ 41 if(bit & pw) ans = (1ll*ans*A)%mod; 42 A = (1ll*A*A)%mod; 43 bit<<=1; 44 } 45 return ans; 46 } 47 48 void divide(int now){ 49 int last = minn[a[now]],Numlast = 0; 50 int p = a[now]; 51 while(p != 1){ 52 if(minn[p] == last) Numlast++; 53 else{ 54 Control[flag[last]].push_back(make_pair(now,Numlast)); 55 last = minn[p];Numlast = 1; 56 } 57 p /= minn[p]; 58 } 59 if(last){Control[flag[last]].push_back(make_pair(now,Numlast));} 60 } 61 62 void dfs(int now,int f,int dp){ 63 dep[now] = dp; fa[now] =f;dfsin[now] = ++num; 64 euler[++nE] = now;app[now][0] = app[now][1] = nE; 65 divide(now); 66 for(RI i=0;i<g[now].size();i++){ 67 if(g[now][i] == f) continue; 68 dfs(g[now][i],now,dp+1); 69 euler[++nE] = now; app[now][1] = nE; 70 } 71 } 72 73 int QueryLCA(int u,int v){ 74 int minn = min(app[u][0],app[v][0]),maxx = max(app[u][1],app[v][1]); 75 int k = 0; while((1<<k+1) <= (maxx-minn+1)) k++; 76 if(dep[RMQ[minn][k]] < dep[RMQ[maxx-(1<<k)+1][k]]){ 77 return RMQ[minn][k]; 78 }else return RMQ[maxx-(1<<k)+1][k]; 79 } 80 81 void BuildRMQ(){ 82 for(RI i=1;i<=nE;i++) RMQ[i][0] = euler[i]; 83 for(RI k=1;(1<<k)<=nE;k++) 84 for(RI i=1;i<=nE;i++){ 85 if(i+(1<<k) > nE) RMQ[i][k] = RMQ[i][k-1]; 86 else{ 87 if(dep[RMQ[i][k-1]] < dep[RMQ[i+(1<<k-1)][k-1]]) 88 RMQ[i][k] = RMQ[i][k-1]; 89 else RMQ[i][k] = RMQ[i+(1<<k-1)][k-1]; 90 } 91 } 92 } 93 94 void init(){ 95 flag[1] = 1; minn[1] = 1; 96 for(RI i=1;i<=N;i++){ 97 if(!flag[i]){prime[++num]=i;flag[i]=num;minn[i]=i;} 98 for(RI j=1;j<=num&&i*prime[j]<=N;j++){ 99 flag[i*prime[j]] = 1; 100 minn[i*prime[j]] = prime[j]; 101 if(i%prime[j] == 0) break; 102 } 103 } 104 dfs(1,0,1); 105 BuildRMQ(); 106 } 107 108 void read(){ 109 fast_in(n); 110 for(RI i=1;i<n;i++){ 111 int u,v;fast_in(u),fast_in(v); 112 g[u].push_back(v); g[v].push_back(u); 113 } 114 for(RI i=1;i<=n;i++) fast_in(a[i]); 115 fast_in(q); 116 } 117 118 int cmp(pair<int,int> alpha,pair<int,int> beta){ 119 if(dfsin[alpha.first] < dfsin[beta.first]) return 1; 120 else return 0; 121 } 122 123 int divisor(int now,int prm){ 124 int len = 0; 125 while(now%prm==0)len++,now/=prm; 126 return len; 127 } 128 129 void AddPoint(int last,int now,int tl,int tr,int data){ 130 if(tl == tr){ 131 if(data) CMT[now].len = CMT[last].len+1; 132 else CMT[now].len = CMT[last].len; 133 CMT[now].tot = CMT[last].tot+data; 134 return; 135 } 136 int mid = (tl+tr)/2; 137 if(data <= mid){ 138 CMT[now].ch[1] = CMT[last].ch[1]; CMT[now].ch[0] = ++numCMT; 139 AddPoint(CMT[last].ch[0],CMT[now].ch[0],tl,mid,data); 140 if(data) CMT[now].len = CMT[last].len+1; 141 else CMT[now].len = CMT[last].len; 142 CMT[now].tot = CMT[last].tot+data; 143 }else{ 144 CMT[now].ch[0] = CMT[last].ch[0]; CMT[now].ch[1] = ++numCMT; 145 AddPoint(CMT[last].ch[1],CMT[now].ch[1],mid+1,tr,data); 146 if(data) CMT[now].len = CMT[last].len+1; 147 else CMT[now].len = CMT[last].len; 148 CMT[now].tot = CMT[last].tot+data; 149 } 150 } 151 152 void dfsup(int now,int pm,int Bel){ 153 if(Vfa[now] == 0){ 154 Gotin[pm][Bel] = ++numCMT; 155 AddPoint(0,numCMT,1,23,Fin[pm][Bel].second); 156 return; 157 } 158 if(!Gotin[pm][Vfuck[now]]){ 159 dfsup(Vfa[now],pm,Vfuck[now]); 160 } 161 if(Fin[pm][Bel].second == 0) Gotin[pm][Bel] = Gotin[pm][Vfuck[now]]; 162 else{ 163 Gotin[pm][Bel] = ++numCMT; 164 AddPoint(Gotin[pm][Vfuck[now]],Gotin[pm][Bel],1,23,Fin[pm][Bel].second); 165 } 166 } 167 168 stack<int> sta; 169 170 void BuildVirtualTree(){ 171 for(RI i=1;i<=num;i++){ 172 int SIZE = Control[i].size(); 173 if(SIZE) Fin[i].push_back(Control[i][0]); 174 for(RI j=1;j<SIZE;j++){ 175 Fin[i].push_back(Control[i][j]); 176 int ff = QueryLCA(Control[i][j].first,Control[i][j-1].first); 177 Fin[i].push_back(make_pair(ff,divisor(a[ff],prime[i]))); 178 } 179 sort(Fin[i].begin(),Fin[i].end(),cmp); 180 SIZE = unique(Fin[i].begin(),Fin[i].end())-Fin[i].begin(); 181 while(Fin[i].size() != SIZE) Fin[i].pop_back(); 182 for(RI j=0;j<SIZE;j++) Vds[i].push_back(++vnum),Vdfs[i].push_back(dfsin[Fin[i][j].first]); 183 for(RI j=0;j<SIZE;j++) Gotin[i].push_back(0); 184 for(RI j=0;j<SIZE;j++){ 185 int jpts = Fin[i][j].first; 186 while(!sta.empty()&&QueryLCA(Fin[i][sta.top()].first,jpts)!=Fin[i][sta.top()].first)sta.pop(); 187 if(!sta.empty()) Vfuck[Vds[i][j]] = sta.top(),Vfa[Vds[i][j]] = Vds[i][sta.top()]; 188 sta.push(j); 189 } 190 while(!sta.empty()) sta.pop(); 191 for(RI j=0;j<SIZE;j++) if(!Gotin[i][j]) dfsup(Vds[i][j],i,j); 192 } 193 } 194 195 int QueryonTree(int now,int tl,int tr,int tag){ 196 if(tl >= tag) return CMT[now].len*tag; 197 if(tr <= tag) return CMT[now].tot; 198 int mid = (tl+tr)/2; 199 return QueryonTree(CMT[now].ch[0],tl,mid,tag)+QueryonTree(CMT[now].ch[1],mid+1,tr,tag); 200 } 201 202 int Query(int now,int im,int pts){ 203 int TreeNum = flag[now]; 204 int p=lower_bound(Vdfs[TreeNum].begin(),Vdfs[TreeNum].end(),dfsin[pts])-Vdfs[TreeNum].begin(); 205 return fast_pow(now,QueryonTree(Gotin[TreeNum][p],1,23,im)); // 23 is const num 5E6 < 2^23 < 1E7 206 } 207 208 void work(){ 209 //puts("b"); 210 for(RI i=1;i<=q;i++){ 211 fast_in(AQ[i].u);fast_in(AQ[i].v);fast_in(AQ[i].x); 212 int u = AQ[i].u,v=AQ[i].v,x=AQ[i].x; 213 int lca = QueryLCA(u,v),last = minn[x],lastNum = 0,ans = 1; 214 while(x!=1){ 215 if(last == minn[x]) lastNum++; 216 else{ 217 Control[flag[last]].push_back(make_pair(u,divisor(a[u],last))); 218 Control[flag[last]].push_back(make_pair(v,divisor(a[v],last))); 219 Control[flag[last]].push_back(make_pair(lca,divisor(a[lca],last))); 220 if(fa[lca])Control[flag[last]].push_back(make_pair(fa[lca],divisor(a[fa[lca]],last))); 221 last = minn[x],lastNum = 1; 222 } 223 x/=minn[x]; 224 } 225 Control[flag[last]].push_back(make_pair(u,divisor(a[u],last))); 226 Control[flag[last]].push_back(make_pair(v,divisor(a[v],last))); 227 Control[flag[last]].push_back(make_pair(lca,divisor(a[lca],last))); 228 if(fa[lca])Control[flag[last]].push_back(make_pair(fa[lca],divisor(a[fa[lca]],last))); 229 } 230 for(RI i=1;i<=num;i++){ 231 sort(Control[i].begin(),Control[i].end(),cmp); 232 int SIZE = unique(Control[i].begin(),Control[i].end())-Control[i].begin(); 233 while(Control[i].size()!=SIZE)Control[i].pop_back(); 234 } 235 //puts("h"); 236 BuildVirtualTree(); 237 for(RI i=1;i<=q;i++){ 238 int u = AQ[i].u,v=AQ[i].v,x=AQ[i].x; 239 int lca = QueryLCA(u,v),last = minn[x],lastNum = 0,ans = 1; 240 while(x!=1){ 241 if(last == minn[x]) lastNum++; 242 else{ 243 ans = (1ll*ans*Query(last,lastNum,u)) % mod; 244 ans = (1ll*ans*Query(last,lastNum,v)) % mod; 245 ans = (1ll*ans*fast_pow(Query(last,lastNum,lca),mod-2))%mod; 246 if(fa[lca]) ans = (1ll*ans*fast_pow(Query(last,lastNum,fa[lca]),mod-2))%mod; 247 last = minn[x],lastNum = 1; 248 } 249 x/=minn[x]; 250 } 251 ans = (1ll*ans*Query(last,lastNum,u)) % mod; 252 ans = (1ll*ans*Query(last,lastNum,v)) % mod; 253 ans = (1ll*ans*fast_pow(Query(last,lastNum,lca),mod-2))%mod; 254 if(fa[lca]) ans = (1ll*ans*fast_pow(Query(last,lastNum,fa[lca]),mod-2))%mod; 255 printf("%d\n",ans); 256 } 257 } 258 259 int main(){ 260 read(); 261 init(); 262 work(); 263 return 0; 264 }
以上是关于Codeforces986E Prince's Problem 虚树可持久化线段树树状数组的主要内容,如果未能解决你的问题,请参考以下文章