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 ...
随机推荐
- 阻止form表单中的input按下回车时提交表单
给form加属性:onsubmit="return false;"
- Makefile有三个非常有用的变量。分别是$@,$^,$
原文地址:https://blog.csdn.net/u013774102/article/details/79043559 假设我们有下面这样的一个程序,源代码如下: /* main.c */ #i ...
- Golang-教程
http://www.runoob.com/go/go-environment.html https://www.pythonav.cn/
- 机器学习第一篇——最近邻kNN
机器学习监督学习中,根据解决问题的连续性和离散型,分为分类问题和回归问题.最邻近算法kNN是一种最为直接和简便的分类方法. kNN本质上,是计算目标到模型的欧式距离,从而判定目标所属的类别. 首先,在 ...
- NEST.net Client
NEST.net Client For Elasticsearch简单应用 由于最近的一个项目中的搜索部分要用到 Elasticsearch 来实现搜索功能,苦于英文差及该方面的系统性资料不好找,在实 ...
- 在IDEA中配置Spring的XML装配
不考虑混合模式的话,Spring有三类装配Bean的方法,自动装配和Java代码装配都会很容易上手,但在弄XML装配时遇到了问题,这与IDEA环境有关. 装配时需要在源码中配置XML文件的位置,我看别 ...
- 【学习总结】GirlsInAI ML-diary day-4:变量/Variable
[学习总结]GirlsInAI ML-diary 总 原博github链接-day4 变量/Variable 变量是计算机编程中一个很基础的概念,在计算机程序中,variables are reser ...
- selenium模拟登陆淘宝
from selenium import webdriver import time from selenium.webdriver.common.by import By from selenium ...
- Webbench、ab命令:做压力测试的工具和性能的监控工具
DDOS攻击:???DDOS概述:分布式拒绝服务(DDoS:Distributed Denial of Service)攻击,指借助于客户/服务器技术,将多个计算机联合起来作为攻击平台,对一个或多个目 ...
- Python3练习题 006 冒泡排序
import random a = [random.randint(1,100) for i in range(10)]def bu(target): length = len(target) whi ...