LuoguP2633 Count on a tree
题意
给定一棵
其中
对于
题解
树上主席树板子。全程独立码完,感觉主席树的代码应该是没问题了,只是还缺乏一些应用的练习。
众所周知查询第 k 小的主席树本质上是个前缀和。
众所周知这个世界上有个叫树上前缀和的东西。
众所周知常规的树上前缀和的式子是
然后就做完了。是不是特别容易呢?
const ll maxn=1e5+5;
struct node{
ll val,ls,rs;
};
ll n;
namespace hjt{
#define mid ((l+r)>>1)
node t[maxn*20];
ll cnt=0;
ll get(){
cnt++;
t[cnt].val=0;
t[cnt].ls=t[cnt].rs=-1;
return cnt;
}
void upd(ll x){
t[x].val=0;
if(t[x].ls>=0)t[x].val+=t[t[x].ls].val;
if(t[x].rs>=0)t[x].val+=t[t[x].rs].val;
}
void build(ll x,ll l,ll r){
if(l==r){
return ;
}
t[x].ls=get();
t[x].rs=get();
build(t[x].ls,l,mid);
build(t[x].rs,mid+1,r);
}
void init(){
for(ll i=0;i<=n;i++)get();
//cerr<<cnt<<endl;
build(0,1,n);
//cerr<<cnt<<endl;
}
void insert(ll u,ll v,ll l,ll r,ll k){
if(l==r){
t[v].val++;
return ;
}
if(k<=mid){
t[v].rs=t[u].rs;
t[v].ls=get();
insert(t[u].ls,t[v].ls,l,mid,k);
}
else {
t[v].ls=t[u].ls;
t[v].rs=get();
insert(t[u].rs,t[v].rs,mid+1,r,k);
}
upd(v);
// cerr<<u<<" "<<v<<" "<<t[v].val<<endl;
}
ll query(ll u,ll v,ll lc,ll fa,ll l,ll r,ll k){
if(l==r){
return l;
}
if(k<=t[t[u].ls].val+t[t[v].ls].val-t[t[lc].ls].val-t[t[fa].ls].val){
return query(t[u].ls,t[v].ls,t[lc].ls,t[fa].ls,l,mid,k);
}
return query(t[u].rs,t[v].rs,t[lc].rs,t[fa].rs,mid+1,r,k-(t[t[u].ls].val+t[t[v].ls].val-t[t[lc].ls].val-t[t[fa].ls].val));
}
#undef mid
}
ll a[maxn],b[maxn],tot;
vector<ll>e[maxn];
namespace lca{
ll fa[maxn][20],dep[maxn];
void dfs(ll x){
dep[x]=dep[fa[x][0]]+1;
hjt::insert(fa[x][0],x,1,n,a[x]);
// cerr<<fa[x][0]<<" "<<x<<" "<<a[x]<<endl;
//cerr<<x<<" "<<hjt::t[x].val<<" "<<hjt::t[fa[x][0]].val<<endl;
for(ll i=1;i<=17;i++){
fa[x][i]=fa[fa[x][i-1]][i-1];
if(!fa[x][i])break;
}
for(auto v:e[x]){
if(v==fa[x][0])continue;
fa[v][0]=x;
dfs(v);
}
}
ll query(ll u,ll v){
if(dep[u]<dep[v])swap(u,v);
for(ll i=17;i>=0;i--){
if(dep[fa[u][i]]>=dep[v])u=fa[u][i];
}
if(u==v)return u;
for(ll i=17;i>=0;i--){
if(fa[u][i]!=fa[v][i])u=fa[u][i],v=fa[v][i];
}
return fa[u][0];
}
}
ll m,lst;
void solve(){
n=R,m=R;
for(ll i=1;i<=n;i++){
a[i]=b[i]=R;
}
sort(b+1,b+1+n);
tot=unique(b+1,b+1+n)-b-1;
for(ll i=1;i<=n;i++){
a[i]=lower_bound(b+1,b+1+tot,a[i])-b;
//cerr<<a[i]<<endl;
}
hjt::init();
for(ll i=1;i<n;i++){
ll u=R,v=R;
e[u].push_back(v);
e[v].push_back(u);
}
lca::dfs(1);
for(ll i=0;i<=n;i++){
// cerr<<i<<" "<<lca::fa[i][0]<<" "<<hjt::t[i].val<<endl;
//cerr<<lca::fa[i][0]<<endl;
}
while(m--){
ll u=R,v=R,k=R;
u^=lst;
//cerr<<u<<" "<<v<<endl;
ll lc=lca::query(u,v);
//cerr<<<<endl;
lst=b[hjt::query(u,v,lc,lca::fa[lc][0],1,n,k)];
we(lst);
}
return ;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通