P4216 [SCOI2015]情报传递 LCA+树上主席树 离线操作
Posted 昵称很长很长真是太好了
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P4216 [SCOI2015]情报传递 LCA+树上主席树 离线操作相关的知识,希望对你有一定的参考价值。
题意:
给你一棵n个点的树,初始每个位置没有点权
有m次操作
1 x:让一个点从当前时刻开始,每秒操作点权++
2 x y c:查询一条链中有多少点的点权大于c
其中每秒操作点权++就是指我每操作一次,无论是否和那个点有
关,那个点权值都会++
1操作对于每个点只会开始一次
题解:
此题的做法非常多
大部分都是
O
(
m
(
l
o
g
n
)
2
)
O(m(logn)^2)
O(m(logn)2)的做法,也就是很朴素的树剖+线段树离线询问。
学习了一种
O
(
m
l
o
g
n
)
O(mlogn)
O(mlogn)的做法,十分巧妙。
看到查询区间权值大于xxx的个数,肯定就是主席树了。
但是我们发现这个题目的点是不断更新的,但是主席树带修改的话就会多一个log,发现这个询问是离线的,那么我们可以先把情报员的点给存起来,然后一边建树的时候就给他加上即可。
如何建立主席树,在树上建立即可,每次查询是查询一条链的结点个数。
是从当前结点到根结点建立主席树。(原来的序列变成了许多条条链)
这样的话我们查询就可以先求出两个点的lca。
然后分别查询这两个点分别到lca这两条链上符合条件的点的个数,然后加起来即可。
代码:
#include<bits/stdc++.h>
#define endl '\\n'
using namespace std;
const int maxn=4e5+10;
vector<int> edge[maxn];
int root;
int fa[maxn],top[maxn],son[maxn];
int dep[maxn],siz[maxn];
void dfs1(int u,int f){
fa[u]=f;
dep[u]=dep[f]+1;
siz[u]=1;
for(auto i:edge[u]){
dfs1(i,u);
siz[u]+=siz[i];
if(siz[i]>siz[son[u]]) son[u]=i;
}
}
void dfs2(int u,int t){
top[u]=t;
if(!son[u]) return ;
dfs2(son[u],t);
for(auto i:edge[u]){
if(i==son[u]) continue;
dfs2(i,i);
}
}
int LCA(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
x=fa[top[x]];
}
return dep[x]<dep[y]?x:y;
}
int ls[maxn*21],rs[maxn*21];
int rt[maxn],sum[maxn*21];
int q,a[maxn],cnt;
void update(int &node,int start,int ends,int lst,int pos,int val){
//cout<<"debug "<<start<<" "<<ends<<" "<<lst<<" "<<pos<<" "<<val<<endl;
node=++cnt;
ls[node]=ls[lst];
rs[node]=rs[lst];
sum[node]=sum[lst]+val;
if(start==ends) return ;
int mid=(start+ends)>>1;
if(pos<=mid) update(ls[node],start,mid,ls[lst],pos,val);
else update(rs[node],mid+1,ends,rs[lst],pos,val);
}
int query(int start,int ends,int l,int r,int x,int y){
if(l<=start&&ends<=r){
return sum[y]-sum[x];
}
int mid=start+ends>>1;
int ans=0;
if(l<=mid) ans+=query(start,mid,l,r,ls[x],ls[y]);
if(mid<r) ans+=query(mid+1,ends,l,r,rs[x],rs[y]);
return ans;
}
int pre[maxn];
void build(int x){
if(a[x]!=0) update(rt[x],1,q,rt[fa[x]],a[x],1);
else update(rt[x],1,q,rt[fa[x]],a[x],0);
for(auto i:edge[x]){
build(i);
}
}
struct E{
int x,y,c;
};
vector<E> v;
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin>>n;
for(int i=1;i<=n;i++){
int x;
cin>>x;
if(x==0) root=i;
else edge[x].push_back(i);
}
cin>>q;
for(int i=1;i<=q;i++){
int opt;
cin>>opt;
if(opt==1){
int x,y,c;
cin>>x>>y>>c;
c=i-c-1;
v.push_back({x,y,c});
}
else{
int t;
cin>>t;
a[t]=i;
}
}
dfs1(root,0);
dfs2(root,root);
build(root);
for(int i=0;i<v.size();i++){
int ans=0;
int x=v[i].x,y=v[i].y;
int lca=LCA(x,y);
//cout<<"lca "<<lca<<" "<<x<<" "<<y<<endl;
ans+=dep[x]+dep[y]-2*dep[lca]+1;
//if(a[lca]!=0) ans++;
cout<<ans<<" ";
ans=0;
//cout<<"debug "<<lca<<" "<<v[i].c<<endl;
if(v[i].c<=0){
cout<<0<<endl;
continue;
}
ans+=query(1,q,1,v[i].c,rt[lca],rt[x]);
ans+=query(1,q,1,v[i].c,rt[lca],rt[y]);
if(a[lca]<=v[i].c&&a[lca]!=0) ans++;
cout<<ans<<endl;
}
}
以上是关于P4216 [SCOI2015]情报传递 LCA+树上主席树 离线操作的主要内容,如果未能解决你的问题,请参考以下文章