[CTSC2008]网络管理Network

## [ 传送门 ](https://www.lydsy.com/JudgeOnline/problem.php?id=1146)

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!

原文地址:https://www.cnblogs.com/PaperCloud/p/10152249.html