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(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重跑,返回运行时间。
代码:
#include<bits/stdc++.h>
using namespace std; #define RI register int const int maxm = ;
const int N = ;
const int maxn = +;
const int Primenum = ;
const int mod = ; int prime[Primenum],flag[maxm],minn[maxm],num; int n,q,a[maxn];
vector<int> g[maxn];
vector<pair<int,int> > Control[Primenum],Fin[Primenum]; int RMQ[maxn<<][],euler[maxn<<],nE,app[maxn][]; int dep[maxn],fa[maxn],dfsin[maxn],dfsnum; vector <int> Vdfs[Primenum],Gotin[Primenum]; struct Ask{int u,v,x;}AQ[maxn];
struct node{int len,tot,ch[];}CMT[];
int numCMT; int Vfa[],Vfuck[],vnum;
vector <int> Vds[Primenum]; inline void fast_in(int &x){
x = ; char ch = getchar();
while(ch > '' || ch < '') ch = getchar();
while(ch <= '' && ch >= '') x = x*+ch-'',ch=getchar();
} inline int fast_pow(int now,int pw){
int ans = ,A = now,bit = ;
while(bit <= pw){
if(bit & pw) ans = (1ll*ans*A)%mod;
A = (1ll*A*A)%mod;
bit<<=;
}
return ans;
} inline void divide(int now){
int last = minn[a[now]],Numlast = ;
int p = a[now];
while(p != ){
if(minn[p] == last) Numlast++;
else{
Control[flag[last]].push_back(make_pair(now,Numlast));
last = minn[p];Numlast = ;
}
p /= minn[p];
}
Control[flag[last]].push_back(make_pair(now,Numlast));
} void dfs(int now,int f,int dp){
dep[now] = dp; fa[now] =f;dfsin[now] = ++num;
euler[++nE] = now;app[now][] = app[now][] = nE;
divide(now);
for(RI i=;i<g[now].size();++i){
if(g[now][i] == f) continue;
dfs(g[now][i],now,dp+);
euler[++nE] = now; app[now][] = nE;
}
} inline int QueryLCA(int u,int v){
int minn = min(app[u][],app[v][]),maxx = max(app[u][],app[v][]);
int k = ; while((<<k+) <= (maxx-minn+)) k++;
if(dep[RMQ[minn][k]] < dep[RMQ[maxx-(<<k)+][k]]){
return RMQ[minn][k];
}else return RMQ[maxx-(<<k)+][k];
} inline void BuildRMQ(){
for(RI i=;i<=nE;++i) RMQ[i][] = euler[i];
for(RI k=;(<<k)<=nE;k++)
for(RI i=;i<=nE;++i){
if(i+(<<k) > nE) RMQ[i][k] = RMQ[i][k-];
else{
if(dep[RMQ[i][k-]] < dep[RMQ[i+(<<k-)][k-]])
RMQ[i][k] = RMQ[i][k-];
else RMQ[i][k] = RMQ[i+(<<k-)][k-];
}
}
} inline void init(){
flag[] = ; minn[] = ;
for(RI i=;i<=N;++i){
if(!flag[i]){prime[++num]=i;flag[i]=num;minn[i]=i;}
for(RI j=;j<=num&&i*prime[j]<=N;++j){
flag[i*prime[j]] = ;
minn[i*prime[j]] = prime[j];
if(i%prime[j] == ) break;
}
}
dfs(,,);
BuildRMQ();
} inline void read(){
fast_in(n);
for(RI i=;i<n;++i){
int u,v;fast_in(u),fast_in(v);
g[u].push_back(v); g[v].push_back(u);
}
for(RI i=;i<=n;++i) fast_in(a[i]);
fast_in(q);
} int cmp(pair<int,int> alpha,pair<int,int> beta){
if(dfsin[alpha.first] < dfsin[beta.first]) return ;
else return ;
} inline int divisor(int now,int prm){
int len = ;
while(now%prm==)len++,now/=prm;
return len;
} inline void AddPoint(int last,int now,int tl,int tr,int data){
if(tl == tr){
if(data) CMT[now].len = CMT[last].len+;
else CMT[now].len = CMT[last].len;
CMT[now].tot = CMT[last].tot+data;
return;
}
int mid = (tl+tr)/;
if(data <= mid){
CMT[now].ch[] = CMT[last].ch[]; CMT[now].ch[] = ++numCMT;
AddPoint(CMT[last].ch[],CMT[now].ch[],tl,mid,data);
if(data) CMT[now].len = CMT[last].len+;
else CMT[now].len = CMT[last].len;
CMT[now].tot = CMT[last].tot+data;
}else{
CMT[now].ch[] = CMT[last].ch[]; CMT[now].ch[] = ++numCMT;
AddPoint(CMT[last].ch[],CMT[now].ch[],mid+,tr,data);
if(data) CMT[now].len = CMT[last].len+;
else CMT[now].len = CMT[last].len;
CMT[now].tot = CMT[last].tot+data;
}
} int sta[maxn],tp=; inline void BuildVirtualTree(){
for(RI i=;i<=num;++i){
sort(Control[i].begin(),Control[i].end(),cmp);
int SIZE = Control[i].size();
if(SIZE) Fin[i].push_back(Control[i][]);
for(RI j=;j<SIZE;++j){
Fin[i].push_back(Control[i][j]);
int ff = QueryLCA(Control[i][j].first,Control[i][j-].first);
Fin[i].push_back(make_pair(ff,divisor(a[ff],prime[i])));
}
sort(Fin[i].begin(),Fin[i].end(),cmp);
SIZE = unique(Fin[i].begin(),Fin[i].end())-Fin[i].begin();
while(Fin[i].size() != SIZE) Fin[i].pop_back();
for(RI j=;j<SIZE;++j) Vds[i].push_back(++vnum),Vdfs[i].push_back(dfsin[Fin[i][j].first]);
for(RI j=;j<SIZE;++j) Gotin[i].push_back();
for(RI j=;j<SIZE;++j){
int jpts = Fin[i][j].first;
while(tp&&QueryLCA(Fin[i][sta[tp]].first,jpts)!=Fin[i][sta[tp]].first)tp--;
if(tp) Vfuck[Vds[i][j]] = sta[tp],Vfa[Vds[i][j]] = Vds[i][sta[tp]];
sta[++tp]=j;
}
tp = ;
for(RI j=;j<SIZE;++j) {
if(Gotin[i][j]) continue;
int now = Vds[i][j];
if(Fin[i][j].second == ) Gotin[i][j] = Gotin[i][Vfuck[now]];
else{
Gotin[i][j] = ++numCMT;
AddPoint(Gotin[i][Vfuck[now]],Gotin[i][j],,,Fin[i][j].second);
}
}
}
} int QueryonTree(int now,int tl,int tr,int tag){
if(tl >= tag) return CMT[now].len*tag;
if(tr <= tag) return CMT[now].tot;
int mid = (tl+tr)/;
return QueryonTree(CMT[now].ch[],tl,mid,tag)+QueryonTree(CMT[now].ch[],mid+,tr,tag);
} inline int Query(int now,int im,int pts){
int TreeNum = flag[now];
int p=lower_bound(Vdfs[TreeNum].begin(),Vdfs[TreeNum].end(),dfsin[pts])-Vdfs[TreeNum].begin();
return fast_pow(now,QueryonTree(Gotin[TreeNum][p],,,im)); // 23 is const num 5E6 < 2^23 < 1E7
} inline void work(){
//puts("b");
for(RI i=;i<=q;++i){
fast_in(AQ[i].u);fast_in(AQ[i].v);fast_in(AQ[i].x);
int u = AQ[i].u,v=AQ[i].v,x=AQ[i].x;
int lca = QueryLCA(u,v),last = minn[x],lastNum = ,ans = ;
while(x!=){
if(last == minn[x]) lastNum++;
else{
if(a[u]%last!=)Control[flag[last]].push_back(make_pair(u,));
if(a[v]%last!=)Control[flag[last]].push_back(make_pair(v,));
last = minn[x],lastNum = ;
}
x/=minn[x];
}
if(a[u]%last!=)Control[flag[last]].push_back(make_pair(u,));
if(a[v]%last!=)Control[flag[last]].push_back(make_pair(v,));
}
//puts("h");
BuildVirtualTree();
for(RI i=;i<=q;++i){
int u = AQ[i].u,v=AQ[i].v,x=AQ[i].x;
int lca = QueryLCA(u,v),last = minn[x],lastNum = ,ans = ;
while(x!=){
if(last == minn[x]) lastNum++;
else{
ans = (1ll*ans*Query(last,lastNum,u)) % mod;
ans = (1ll*ans*Query(last,lastNum,v)) % mod;
int zz = fast_pow(Query(last,lastNum,lca),mod-);zz = (1ll*zz*zz)%mod;
ans = (1ll*ans*zz)%mod;
last = minn[x],lastNum = ;
}
x/=minn[x];
}
ans = (1ll*ans*Query(last,lastNum,u)) % mod;
ans = (1ll*ans*Query(last,lastNum,v)) % mod;
int zz = fast_pow(Query(last,lastNum,lca),mod-);zz = (1ll*zz*zz)%mod;
ans = (1ll*ans*zz)%mod;
ans = (1ll*ans*__gcd(a[lca],AQ[i].x))%mod;
printf("%d\n",ans);
}
} int main(){
read();
init();
work();
return ;
}
Codeforces986E Prince's Problem 【虚树】【可持久化线段树】【树状数组】的更多相关文章
- 主席树||可持久化线段树+离散化 || 莫队+分块 ||BZOJ 3585: mex || Luogu P4137 Rmq Problem / mex
题面:Rmq Problem / mex 题解: 先离散化,然后插一堆空白,大体就是如果(对于以a.data<b.data排序后的A)A[i-1].data+1!=A[i].data,则插一个空 ...
- [BZOJ 4771]七彩树(可持久化线段树+树上差分)
[BZOJ 4771]七彩树(可持久化线段树+树上差分) 题面 给定一棵n个点的有根树,编号依次为1到n,其中1号点是根节点.每个节点都被染上了某一种颜色,其中第i个节点的颜色为c[i].如果c[i] ...
- 【BZOJ-3218】a+b Problem 最小割 + 可持久化线段树
3218: a + b Problem Time Limit: 20 Sec Memory Limit: 40 MBSubmit: 1320 Solved: 498[Submit][Status] ...
- [POJ2104/HDU2665]Kth Number-主席树-可持久化线段树
Problem Kth Number Solution 裸的主席树,模板题.但是求k大的时候需要非常注意,很多容易写错的地方.卡了好久.写到最后还给我来个卡空间. 具体做法参见主席树论文<可持久 ...
- BZOJ 3483 SGU505 Prefixes and suffixes(字典树+可持久化线段树)
[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3483 [题目大意] 给出一些串,同时给出m对前缀后缀,询问有多少串满足给出的前缀后缀模 ...
- 归并树 划分树 可持久化线段树(主席树) 入门题 hdu 2665
如果题目给出1e5的数据范围,,以前只会用n*log(n)的方法去想 今天学了一下两三种n*n*log(n)的数据结构 他们就是大名鼎鼎的 归并树 划分树 主席树,,,, 首先来说两个问题,,区间第k ...
- 主席树[可持久化线段树](hdu 2665 Kth number、SP 10628 Count on a tree、ZOJ 2112 Dynamic Rankings、codeforces 813E Army Creation、codeforces960F:Pathwalks )
在今天三黑(恶意评分刷上去的那种)两紫的智推中,突然出现了P3834 [模板]可持久化线段树 1(主席树)就突然有了不详的预感2333 果然...然后我gg了!被大佬虐了! hdu 2665 Kth ...
- BZOJ.4771.七彩树(可持久化线段树)
BZOJ 考虑没有深度限制,对整棵子树询问怎么做. 对于同种颜色中DFS序相邻的两个点\(u,v\),在\(dfn[u],dfn[v]\)处分别\(+1\),\(dfn[LCA(u,v)]\)处\(- ...
- BZOJ4771七彩树——可持久化线段树+set+树链的并+LCA
给定一棵n个点的有根树,编号依次为1到n,其中1号点是根节点.每个节点都被染上了某一种颜色,其中第i个节 点的颜色为c[i].如果c[i]=c[j],那么我们认为点i和点j拥有相同的颜色.定义dept ...
随机推荐
- urllib爬虫(流程+案例)
网络爬虫是一种按照一定规则自动抓取万维网信息的程序.在如今网络发展,信息爆炸的时代,信息的处理变得尤为重要.而这之前就需要获取到数据.有关爬虫的概念可以到网上查看详细的说明,今天在这里介绍一下使用ur ...
- Jmeter性能与接口自动化实战
本书可以作为工具书,也可以作为jmeter疑难杂症的良方!各位同学在jmeter学习中遇到的常见难题,在本文中都可以检索到答案. 大纲如下: ☞点击这里,获取本书
- 小程序wxRequest封装
//const host = 'http://114.215.00.00:8005';// 测试地址 const host = 'https://xx.xxxxxxxx.net'; // 正式地址 c ...
- elasticSearch聚合sum查询
有时需要统计一段时间内,订单的总金额.类似于sql的sum,针对某一字段求和.这就涉及到es的聚合查询,来看看用spring-data-elasticSearch怎么写: QueryBuilder ...
- python札记
进制转换 num = "0011"v = int(num, base=16)print(v)2->16
- 硬盘扩容9999T
win+r运行创建命令:subst H: d:\123说明:H指的是想要创建的盘符,d:\123是文件路径 删除命令subst H: d/说明 :H指的是已创建的盘符,/d指的是删除的意思 注意新盘符 ...
- Python3练习题 021:递归方法求阶乘
利用递归方法求5!. 方法一 f = 1 for i in range(1,6): f = f * i print(f) 方法二 import functools print(functo ...
- React Native之获取通讯录信息并实现类通讯录列表(ios android)
React Native之获取通讯录信息并实现类通讯录列表(ios android) 一,需求分析 1,获取通讯录信息,筛选出通讯录里有多少好友在使用某个应用. 2,获取通讯录信息,实现类通讯录,可拨 ...
- [转帖]Linux 硬件和系统配置查看命令小结
https://blog.csdn.net/strongwangjiawei/article/details/8208825 转帖了不少 发现自己记住的还是不多.. Linux 硬件和系统配置查看命令 ...
- 使用NFS时的一些问题
当我把nfs服务端共享目录/usr/local/data/test删掉时,在nfs客户端却没办法把之前挂载在这上面的当前从机上的/usr/local/data/test删除,出现 bash: cd: ...