Descroption
带修改求树上路径第k大
Solution
方法一:树状数组套主席树
出题人总是比较喜欢把序列的东西搬到树上去但这并不影响我们做题,因为我们可以用一个log的时间将它变回序列的操作(树链剖分)
使用树状数组,是因为它可以支持单点加和区间求和的操作。
我们求出dfs序,然后按照顺序,将每个点离散后的权值,加到树状数组上去
其实是加到树状数组每个点所对应的权值线段树上
这样,我们处理操作的时候:
- 修改操作:就和之前的加点一样,在原权值处-1,新权值处+1
- 询问操作:找出x和y到它们的lca上的log条重链,每条链都对应dfs序的一个区间,我们先记录下这log个区间,然后在权值线段树上二分,对这些区间的端点加加减减一下,就能求出相应权值区间的数量啦
复杂度是(O(n log^3 n))的
之前求成第k小了,wa了无数次,我真菜
Code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
#define reg register
#define MN 80005
int n,m,val[MN],s[MN<<1],tot;
struct OPT{int l,r,k;}o[MN];
int ls[MN*200],rs[MN*200],t[MN*200],sz,rt[MN];
#define mid ((l+r)>>1)
void Modify(int &rt,int l,int r,int x,int v)
{
++sz;ls[sz]=ls[rt];rs[sz]=rs[rt];t[sz]=t[rt]+v;rt=sz;
if(l==r) return;
if(x<=mid) Modify(ls[rt],l,mid,x,v);
else Modify(rs[rt],mid+1,r,x,v);
}
int tmp[400],cnt,sign[400];
int Query(int l,int r,int k)
{
if(l==r) return l;
register int sum=0,i;
for(i=1;i<=cnt;++i) sum+=t[rs[tmp[i]]]*sign[i];
if(k<=sum)
{
for(i=1;i<=cnt;++i) tmp[i]=rs[tmp[i]];
return Query(mid+1,r,k);
}
else
{
for(i=1;i<=cnt;++i) tmp[i]=ls[tmp[i]];
return Query(l,mid,k-sum);
}
}
#define lb(x) (x&(-x))
inline void C(int x,int p,int v){for(;x<=n;x+=lb(x)) Modify(rt[x],1,tot,p,v);}
struct edge{int to,nex;}e[MN<<1];int hr[MN],en;
inline void ins(int f,int t)
{
e[++en]=(edge){t,hr[f]};hr[f]=en;
e[++en]=(edge){f,hr[t]};hr[t]=en;
}
int dfn[MN],fdfn[MN],mx[MN],top[MN],siz[MN],fa[MN],dep[MN],dind;
void dfs1(int x,int f)
{
dep[x]=dep[fa[x]=f]+1;siz[x]=1;
for(reg int i=hr[x];i;i=e[i].nex) if(f^e[i].to)
dfs1(e[i].to,x),siz[e[i].to]>siz[mx[x]]?mx[x]=e[i].to:0,siz[x]+=siz[e[i].to];
}
void dfs2(int x,int tp)
{
dfn[x]=++dind;fdfn[dind]=x;top[x]=tp;if(mx[x]) dfs2(mx[x],tp);
for(reg int i=hr[x];i;i=e[i].nex) if((fa[x]^e[i].to)&&(mx[x]^e[i].to))
dfs2(e[i].to,e[i].to);
}
inline void Insert(int l,int r)
{
for(;r;r-=lb(r)) tmp[++cnt]=rt[r],sign[cnt]=1;
for(--l;l;l-=lb(l)) tmp[++cnt]=rt[l],sign[cnt]=-1;
}
inline void que(int x,int y,int k)
{
cnt=0;int num=1+dep[x]+dep[y];
while(top[x]^top[y])
{
if(dep[top[x]]>dep[top[y]]) Insert(dfn[top[x]],dfn[x]),x=fa[top[x]];
else Insert(dfn[top[y]],dfn[y]),y=fa[top[y]];
}
if(dep[x]<dep[y]) std::swap(x,y);
Insert(dfn[y],dfn[x]);num-=2*dep[y];
if(num<k) puts("invalid request!");
else printf("%d
",s[Query(1,tot,k)]);
}
int main()
{
n=read();m=read();
register int i,x;
for(i=1;i<=n;++i) ++tot,s[tot]=val[i]=read();
for(i=1;i<n;++i) x=read(),ins(x,read());
for(i=1;i<=m;++i)
{
o[i].k=read();o[i].l=read(),o[i].r=read();
if(!o[i].k) ++tot,s[tot]=o[i].r;
}
std::sort(s+1,s+tot+1);
tot=std::unique(s+1,s+tot+1)-s-1;
for(i=1;i<=n;++i) val[i]=std::lower_bound(s+1,s+tot+1,val[i])-s;
for(i=1;i<=m;++i) if(!o[i].k) o[i].r=std::lower_bound(s+1,s+tot+1,o[i].r)-s;
dfs1(1,0);dfs2(1,1);
for(i=1;i<=n;++i) C(i,val[fdfn[i]],1);
for(i=1;i<=m;++i)
{
if(!o[i].k) C(dfn[o[i].l],val[o[i].l],-1),C(dfn[o[i].l],o[i].r,1),val[o[i].l]=o[i].r;
else que(o[i].l,o[i].r,o[i].k);
}
return 0;
}
方法二:整体二分
仍然是树链剖分
用上整体二分的套路
修改操作可以看成一次加点和一次删点
upd:大概在一周后,终于补上了整体二分的代码......
复杂度仍然是(O(n log^3 n))的
我怎么再次打成了第k小,我是有智力问题吧(疯了)......
Code
#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
#define MN 30002
#define MM 80002
int n,tot,ID,val[MM],Ans[MN];
int num[MN+MM],cnt;
struct opt{int x,y,k,id;}q[MM+(MN<<1)],p[MM+(MN<<1)];
struct edge{int to,nex;}e[MM<<1];int hr[MM],en;
inline void ins(int f,int t)
{
e[++en]=(edge){t,hr[f]};hr[f]=en;
e[++en]=(edge){f,hr[t]};hr[t]=en;
}
int dep[MM],fa[MM],top[MM],mx[MM],sz[MM],dfn[MM],dind;
inline void dfs1(int x=1,int f=0)
{
dep[x]=dep[fa[x]=f]+(sz[x]=1);register int i;
for(i=hr[x];i;i=e[i].nex)if(e[i].to^f) dfs1(e[i].to,x),(sz[e[i].to]>sz[mx[x]])?mx[x]=e[i].to:0,sz[x]+=sz[e[i].to];
}
inline void dfs2(int x=1,int tp=1)
{
dfn[x]=++dind;top[x]=tp;if(mx[x]) dfs2(mx[x],tp);register int i;
for(i=hr[x];i;i=e[i].nex)if((e[i].to^fa[x])&&(e[i].to^mx[x])) dfs2(e[i].to,e[i].to);
}
#define lb(x) (x&(-x))
int t[MM];
inline void C(int x,int v){for(;x<=n;x+=lb(x))t[x]+=v;}
inline int G(int x){int r=0;for(;x;x-=lb(x))r+=t[x];return r;}
inline int get_lca(int x,int y){
for(;top[x]^top[y];)if(dep[top[x]]>dep[top[y]])x=fa[top[x]];else y=fa[top[y]];
return dep[x]>dep[y]?y:x;
}
inline int get_num(int x,int y)
{
int r=0;
for(;top[x]^top[y];)
dep[top[x]]>dep[top[y]]?(r+=G(dfn[x])-G(dfn[top[x]]-1),x=fa[top[x]]):(r+=G(dfn[y])-G(dfn[top[y]]-1),y=fa[top[y]]);
r+=G(max(dfn[x],dfn[y]))-G(min(dfn[x],dfn[y])-1);
return r;
}
void solve(int l=1,int r=cnt,int ql=1,int qr=tot)
{
if(ql>qr) return;
register int i;
if(l==r)
{
for(i=ql;i<=qr;++i) if(q[i].id) Ans[q[i].id]=num[l];
return;
}
register int mid=(l+r+1)>>1,pl=ql,pr=qr,tmp;
for(i=ql;i<=qr;++i)
{
if(!q[i].id)
{
if(q[i].y>=num[mid]) p[pr--]=q[i],C(dfn[q[i].x],q[i].k);
else p[pl++]=q[i];
}
else
{
tmp=get_num(q[i].x,q[i].y);
if(tmp>=q[i].k) p[pr--]=q[i];
else q[i].k-=tmp,p[pl++]=q[i];
}
}
for(i=ql;i<=qr;++i) if(!q[i].id&&q[i].y>=num[mid]) C(dfn[q[i].x],-q[i].k);
for(i=ql;i<pl;++i) q[i]=p[i];
for(i=pl;i<=qr;++i) q[i]=p[qr-i+pl];
solve(l,mid-1,ql,pl-1);solve(mid,r,pr+1,qr);
}
int main()
{
register int i,m,x,k,y;
n=read();m=read();
for(i=1;i<=n;++i) q[++tot]=(opt){i,val[i]=read(),1,0},num[++cnt]=val[i];
for(i=1;i<n;++i) x=read(),ins(x,read());
dfs1();dfs2();
while(m--)
{
k=read();x=read();y=read();
if(!k) q[++tot]=(opt){x,val[x],-1,0},q[++tot]=(opt){x,y,1,0},num[++cnt]=val[x]=y;
else
{
++ID;register int lca=get_lca(x,y);
if(dep[x]+dep[y]-2*dep[lca]+1<k) Ans[ID]=-1;
else q[++tot]=(opt){x,y,k,ID};
}
}
std::sort(num+1,num+cnt+1);
cnt=std::unique(num+1,num+cnt+1)-num-1;
solve();
for(i=1;i<=ID;++i) printf(Ans[i]==-1?"invalid request!
":"%d
",Ans[i]);
return 0;
}
Blog来自PaperCloud,未经允许,请勿转载,TKS!