题意
在一棵有 nn 个节点的树上,求出有多少个点对 ,满足 uu 是 的祖先且 a[u]∗a[v]≤ka[u]∗a[v]≤k 。
1≤n≤1051≤n≤105
0≤ai≤1090≤ai≤109
0≤k≤10180≤k≤1018
思路
解法一:dfs序+归并树
如果把式子做一些变化,改为 a[v]≤ka[u]a[v]≤ka[u] ,再用 dfsdfs 序处理出树节点对应的区间,就是比较裸的归并树了,每次查询以某个节点 uu 为祖先的子树中满足条件的 的个数。
解法二:dfs序+离线归并
查询一个区间内小于等于某个数的数的个数,转化成离线归并+树状数组会比归并树常数小很多。离线归并并不是归并排序,而是对一个有序的询问队列 QQ ,一个有序的更新队列 ,通过类似归并的方式回答 QQ ,并将 的更新记录在数据结构中。像这道题,只用维护一个 0101 的树状数组,00 表示当前位置上没有数, 表示有数。对于一个节点 uu ,它贡献出的询问是 中小于等于 ka[u]ka[u] 的数的个数,贡献出的更新是当询问值首次小于等于 a[u]a[u] 时,将树状数组上的 L[u]L[u] 赋为 11 。那只用将询问和更新的队列按键值从小到大排序,然后当更新队头小于等于询问队头时,执行更新即可,下面是一个比较一般的板子。
int u=1;
FOR(i,1,n)
{
while(u<=n&&U[u].val...Q[i].val)
BIT.update(U[u].x,...),u++; //执行更新
FOR(j,0,(int)Q[i].size()-1)Out[Q[i][j].id]BIT.query(Q[i][j].L,Q[i][j].R,...); //回答询问
}
解法三:dfs栈+树状数组
如果我们将原式再变一下,当 是 vv 的祖先时,求 的 uu 的个数,题目就变成了求合法祖先个数,那只用在遍历到 节点时,将 vv 节点的值在树状数组中更新,再直接在树状数组中查询并更新答案,当回溯上来时,再将 节点信息清除即可,由于至过大,应在离散的基础上更新查询。
解法四:dfs作差+树状数组
这种方法比上一种方法拓展性更强。当 uu 是 的祖先时,我们要求的是合法的 vv 的个数。不难发现,刚遍历到 和从 uu 回溯时,两者信息的差值恰好就是 这棵子树的信息。那我们只需在刚遍历到 uu 时,求出所有满足条件的点的个数,即将从 回溯时,再算一次满足条件点的个数并减去前者,就是 uu 的子树中满足条件点的个数。不难看出,当所求的答案与子树有关且符合作差性质的, 作差是很适合的方法。
代码
解法一:dfs序+归并树
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
#define lowbit(x) ((x)&-(x))
#define N 100003
typedef long long LL;
using namespace std;
template<const int maxn,const int maxm>struct Linked_list
{
int head[maxn],to[maxm],nxt[maxm],tot;
void clear(){memset(head,-1,sizeof(head));tot=0;}
void add(int u,int v){to[++tot]=v,nxt[tot]=head[u],head[u]=tot;}
#define EOR(i,G,u) for(int i=G.head[u];~i;i=G.nxt[i])
};
struct node
{
int L,R;
vector<LL>vec;
void merging(node &x,node &y)
{
vec.clear();
int i=0,j=0;
while(i<x.vec.size()||j<y.vec.size())
{
if(i>=x.vec.size()||j<y.vec.size()&&y.vec[j]<x.vec[i])
vec.push_back(y.vec[j++]);
else vec.push_back(x.vec[i++]);
}
}
int check(LL chk){return upper_bound(vec.begin(),vec.end(),chk)-vec.begin();}
};
struct SegmentTree
{
node nd[N<<2];
void build(int k,int L,int R,LL *arr)
{
nd[k].L=L,nd[k].R=R;
if(L==R)
{
nd[k].vec.clear();
nd[k].vec.push_back(arr[L]);
return;
}
build(k<<1,L,L+R>>1,arr);
build(k<<1|1,(L+R>>1)+1,R,arr);
nd[k].merging(nd[k<<1],nd[k<<1|1]);
}
int query(int k,int L,int R,LL chk)
{
if(L<=nd[k].L&&nd[k].R<=R)return nd[k].check(chk);
int mid=nd[k].L+nd[k].R>>1;
if(R<=mid)return query(k<<1,L,R,chk);
else if(L>mid)return query(k<<1|1,L,R,chk);
else return query(k<<1,L,R,chk)+query(k<<1|1,L,R,chk);
}
};
struct Tree
{
Linked_list<N,N>G;
SegmentTree ST;
int L[N],R[N],ord;
void reset(){G.clear();ord=0;}
void build(int n,LL *arr){ST.build(1,1,n,arr);};
void add(int u,int v){G.add(u,v);}
void dfs(int u,int fa)
{
L[u]=++ord;
EOR(i,G,u)
{
int v=G.to[i];
if(v!=fa)dfs(v,u);
}
R[u]=ord;
}
int query(int k,LL chk){return L[k]==R[k]?0:ST.query(1,L[k]+1,R[k],chk);}
}T;
LL a[N],b[N];
bool root[N];
int main()
{
int Task,n;
LL k,ans;
scanf("%d",&Task);
while(Task--)
{
ans=0;
scanf("%d%lld",&n,&k);
FOR(i,1,n)root[i]=1;
FOR(i,1,n)scanf("%lld",&a[i]);
T.reset();
FOR(i,1,n-1)
{
int u,v;
scanf("%d%d",&u,&v);
T.add(u,v);
root[v]=0;
}
FOR(i,1,n)if(root[i])T.dfs(i,-1);
FOR(i,1,n)b[T.L[i]]=a[i];
T.build(n,b);
FOR(i,1,n)
{
int ans_=ans;
if(a[i]==0)ans+=T.R[i]-T.L[i];
else ans+=T.query(i,k/a[i]);
}
printf("%lld\n",ans);
}
return 0;
}
解法二:dfs序+离线归并
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
#define lowbit(x) ((x)&-(x))
#define N 100003
typedef long long LL;
using namespace std;
int a[N],b[N],L[N],R[N],n,ord;
bool root[N];
LL ans,K;
template<const int maxn,const int maxm>struct Linked_list
{
int head[maxn],to[maxm],nxt[maxm],tot;
void clear(){memset(head,-1,sizeof(head));tot=0;}
void add(int u,int v){to[++tot]=v,nxt[tot]=head[u],head[u]=tot;}
#define EOR(i,G,u) for(int i=G.head[u];~i;i=G.nxt[i])
};
Linked_list<N,N>G;
struct QList
{
int L,R;
LL val;
bool operator <(const QList &_)const
{
return val<_.val;
}
}Q[N];
struct Ulist
{
int x;
LL val;
bool operator <(const Ulist &_)const
{
return val<_.val;
}
}U[N];
struct BinaryIndexedTree
{
int c[N],n;
void reset(int _){memset(c,0,sizeof(c));n=_;}
void update(int k,int val)
{
while(k<=n)
{
c[k]+=val;
k+=lowbit(k);
}
}
int _query(int k)
{
int res=0;
while(k>0)
{
res+=c[k];
k^=lowbit(k);
}
return res;
}
int query(int L,int R){return _query(R)-_query(L-1);}
}BIT;
void dfs(int u,int fa)
{
L[u]=++ord;
EOR(i,G,u)
{
int v=G.to[i];
if(v==fa)continue;
dfs(v,u);
}
R[u]=ord;
}
void solve()
{
int u=1;
FOR(i,1,n)
{
while(u<=n&&U[u].val<=Q[i].val)
BIT.update(U[u++].x,1);
ans+=BIT.query(Q[i].L,Q[i].R);
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
ans=0;
scanf("%d%lld",&n,&K);
FOR(i,1,n)root[i]=1;
FOR(i,1,n)
{
int t;
scanf("%d",&t);
Q[i].val=(t?K/t:5e18);
U[i].val=t;
}
G.clear();
BIT.reset(n);
ord=0;
FOR(i,1,n-1)
{
int u,v;
scanf("%d%d",&u,&v);
G.add(u,v);
root[v]=0;
}
FOR(i,1,n)if(root[i])dfs(i,-1);
FOR(i,1,n)
{
Q[i].L=L[i]+1,Q[i].R=R[i];
U[i].x=L[i];
}
sort(Q+1,Q+1+n);
sort(U+1,U+1+n);
solve();
printf("%lld\n",ans);
}
return 0;
}
解法三:dfs栈+树状数组
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
#define lowbit(x) ((x)&-(x))
#define N 100003
typedef long long LL;
using namespace std;
int a[N],b[N],n;
LL ans,K;
int lbnd(LL k){return lower_bound(b+1,b+1+n,k)-b;}
int ubnd(LL k){return upper_bound(b+1,b+1+n,k)-b;}
template<const int maxn,const int maxm>struct Linked_list
{
int head[maxn],to[maxm],nxt[maxm],tot;
void clear(){memset(head,-1,sizeof(head));tot=0;}
void add(int u,int v){to[++tot]=v,nxt[tot]=head[u],head[u]=tot;}
#define EOR(i,G,u) for(int i=G.head[u];~i;i=G.nxt[i])
};
struct BinaryIndexedTree
{
int c[N],n;
void reset(int _){n=_;memset(c,0,sizeof(c));}
void update(int k,int val)
{
while(k<=n)
{
c[k]+=val;
k+=lowbit(k);
}
}
int query(int k)
{
int res=0;
k=min(k,n);
while(k)
{
res+=c[k];
k^=lowbit(k);
}
return res;
}
};
struct Tree
{
Linked_list<N,N>G;
BinaryIndexedTree BIT;
int p[N],n;
void reset(int _,int *arr)
{
BIT.reset(n=_);G.clear();
FOR(i,1,n)p[i]=arr[i];
}
void add(int u,int v){G.add(u,v);}
void dfs(int u,int fa)
{
if(!p[u])ans+=BIT.query(n);
else ans+=BIT.query(ubnd(K/p[u])-1);
BIT.update(lbnd(p[u]),1);
EOR(i,G,u)
{
int v=G.to[i];
if(v!=fa)dfs(v,u);
}
BIT.update(lbnd(p[u]),-1);
}
}T;
bool root[N];
int main()
{
int Task;
scanf("%d",&Task);
while(Task--)
{
ans=0;
scanf("%d%lld",&n,&K);
FOR(i,1,n)root[i]=1;
FOR(i,1,n)scanf("%lld",&a[i]),b[i]=a[i];
sort(b+1,b+1+n);
T.reset(n,a);
FOR(i,1,n-1)
{
int u,v;
scanf("%d%d",&u,&v);
T.add(u,v);
root[v]=0;
}
FOR(i,1,n)if(root[i])T.dfs(i,-1);
printf("%lld\n",ans);
}
return 0;
}
解法四:dfs作差+树状数组
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
#define lowbit(x) ((x)&-(x))
#define N 100003
typedef long long LL;
using namespace std;
int a[N],b[N],n;
LL ans,K;
int lbnd(LL k){return lower_bound(b+1,b+1+n,k)-b;}
int ubnd(LL k){return upper_bound(b+1,b+1+n,k)-b;}
template<const int maxn,const int maxm>struct Linked_list
{
int head[maxn],to[maxm],nxt[maxm],tot;
void clear(){memset(head,-1,sizeof(head));tot=0;}
void add(int u,int v){to[++tot]=v,nxt[tot]=head[u],head[u]=tot;}
#define EOR(i,G,u) for(int i=G.head[u];~i;i=G.nxt[i])
};
struct BinaryIndexedTree
{
int c[N],n;
void reset(int _){n=_;memset(c,0,sizeof(c));}
void update(int k,int val)
{
while(k<=n)
{
c[k]+=val;
k+=lowbit(k);
}
}
int query(int k)
{
int res=0;
k=min(k,n);
while(k)
{
res+=c[k];
k^=lowbit(k);
}
return res;
}
};
struct Tree
{
Linked_list<N,N>G;
BinaryIndexedTree BIT;
int p[N],n;
void reset(int _,int *arr)
{
BIT.reset(n=_);G.clear();
FOR(i,1,n)p[i]=arr[i];
}
void add(int u,int v){G.add(u,v);}
void dfs(int u,int fa)
{
int cnt=0;
if(!p[u])cnt=BIT.query(n);
else cnt=BIT.query(ubnd(K/p[u])-1);
EOR(i,G,u)
{
int v=G.to[i];
if(v!=fa)dfs(v,u);
}
if(!p[u])ans+=BIT.query(n)-cnt;
else ans+=BIT.query(ubnd(K/p[u])-1)-cnt;
BIT.update(lbnd(p[u]),1);
}
}T;
bool root[N];
int main()
{
int Task;
scanf("%d",&Task);
while(Task--)
{
ans=0;
scanf("%d%lld",&n,&K);
FOR(i,1,n)root[i]=1;
FOR(i,1,n)scanf("%lld",&a[i]),b[i]=a[i];
sort(b+1,b+1+n);
T.reset(n,a);
FOR(i,1,n-1)
{
int u,v;
scanf("%d%d",&u,&v);
T.add(u,v);
root[v]=0;
}
FOR(i,1,n)if(root[i])T.dfs(i,-1);
printf("%lld\n",ans);
}
return 0;
}
博客围绕一棵有n个节点的树,求解满足u是v的祖先且a[u]*a[v]≤k的点对(u,v)数量。介绍了四种解法,包括dfs序+归并树、dfs序+离线归并、dfs栈+树状数组、dfs作差+树状数组,还给出了各解法思路及对应代码。
&spm=1001.2101.3001.5002&articleId=81483825&d=1&t=3&u=16287120e30440e79d72e7200e63dd9b)
294

被折叠的 条评论
为什么被折叠?



