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 虚树可持久化线段树树状数组的主要内容,如果未能解决你的问题,请参考以下文章

[CF986E]Prince's Problem

Prince2是什么PRINCE2认证之PRINCE2的思维结构

PRINCE2认证的优势和原理

Prince2科普Prince2的七大原则

Prince2科普Prince2的七大原则

Prince2是什么PRINCE2认证之Prince2衡量绩效的六大要素