题解 LGP1600【[NOIP2016 提高组] 天天爱跑步】

problem

一棵树,树上 \(m\) 个人从 \(s_i\) 跑到 \(t_i\),速度 \(v=1m/s\),求对于每个点在 \(w_i\) 时刻有多少个人刚好经过。\(n,m\leq 10^5\)

solution 0

考虑一个人如果从 \(u\) 出发往上走,走到 \(k\) 时,时间是 \(dep_u-dep_k\)

考虑从某个时刻 \(T\) 开始时间倒流,从 \(v\) 倒着往上走,走到 \(k\) 时,时间是 \(T-(dep_v-dep_k)=T-dep_v+dep_k\)

移项:\(dep_u=w+dep_k,T-dep_v=w-dep_k\)

树上差分之后线段树合并一下,复杂度 \(O(n\log n)\)

solution 1

题解做法很 NB。

查询子树信息是吧,我在放入子树信息之前先记录现在的外面的子树信息,放完之后再查询一次,做差分就是真实的子树信息。

形式化的:

  • dfs
  • 在往下 dfs 之前查询一下记为 \(ans_1\)
  • dfs 完所有子树之后,查询当前答案记为 \(ans_2\)
  • 那么 \(ans=ans_2-ans_1\)

复杂度 \(O(n)\)(LCA 当作 \(O(1)\)

code 0

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr,##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
template<int N,int M,class T=int> struct graph{
    int head[N+10],nxt[M*2+10],cnt;
    struct edge{
        int u,v;T w;
        edge(int u=0,int v=0,T w=0):u(u),v(v),w(w){}
    } e[M*2+10];
    graph(){memset(head,cnt=0,sizeof head);}
    edge&operator[](int i){return e[i];}
    void add(int u,int v,T w=0){e[++cnt]=edge(u,v,w),nxt[cnt]=head[u],head[u]=cnt;}
    void link(int u,int v,T w=0){add(u,v,w),add(v,u,w);}
};
template<int N,int M,class T=int> struct treecut: public graph<N,M,T>{
    graph<N,M,T> &g=*this;
    int fa[N+10],dep[N+10],son[N+10],siz[N+10],
        dfn[N+10],rnk[N+10],top[N+10],cnt;
    treecut(){memset(son,cnt=siz[0]=0,sizeof son);}
    void dfs(int u,int f=0){
        dep[u]=dep[fa[u]=f]+1,siz[u]=1;
        for(int i=g.head[u];i;i=g.nxt[i]){
            int v=g[i].v; if(v==fa[u]) continue;
            dfs(v,u),siz[u]+=siz[v];
            if(siz[son[u]]<siz[v]) son[u]=v;
        }
    }
    void cut(int u,int topf){
        top[rnk[dfn[u]=++cnt]=u]=topf;
        if(son[u]) cut(son[u],topf);
        for(int i=g.head[u];i;i=g.nxt[i]){
            int v=g[i].v; if(v==fa[u]||v==son[u]) continue;
            cut(v,v);
        }
    }
    int lca(int u,int v){
        for(;top[u]!=top[v];u=fa[top[u]]) if(dep[top[u]]<dep[top[v]]) swap(u,v);
        if(dep[u]<dep[v]) swap(u,v); return v;
    }
    int dist(int u,int v){return dep[u]+dep[v]-2*dep[lca(u,v)];}
};
template<int N> struct segtree{
	//单点加,单点查询,魔怔了 
	int ch[N+10][2],tot,ans[N+10];
	int newnode(){int p=++tot;return ch[p][0]=ch[p][1]=ans[p]=0,p;}
	segtree():tot(-1){newnode();}
	void modify(int x,int k,int &p,int l=-3e5,int r=3e5){
		if(!p) p=newnode();
		if(l==r) return (void)(ans[p]+=k);
		int mid=(l+r)>>1;
		if(x<=mid) modify(x,k,ch[p][0],l,mid);
		else modify(x,k,ch[p][1],mid+1,r);
		ans[p]=ans[ch[p][0]]+ans[ch[p][1]];
	}
	int query(int x,int p,int l=-3e5,int r=3e5){
		if(!p||x<l||r<x) return 0;
		if(l==r) return ans[p];
		int mid=(l+r)>>1;
		return x<=mid?query(x,ch[p][0],l,mid):query(x,ch[p][1],mid+1,r);
	}
	void merge(int &p,int q,int l=-3e5,int r=3e5){
		if(!p||!q) return (void)(p|=q);
		if(l==r) return (void)(ans[p]+=ans[q]);
		int mid=(l+r)>>1;
		merge(ch[p][0],ch[q][0],l,mid);
		merge(ch[p][1],ch[q][1],mid+1,r);
		ans[p]=ans[ch[p][0]]+ans[ch[p][1]];
	}
	void dfs(int p,int l=-3e5,int r=3e5){
		if(!p) return ;
		if(l==r){
			for(int i=1;i<=ans[p];i++) debug("%d ",l);
			return ;
		}
		int mid=(l+r)>>1;
		dfs(ch[p][0],l,mid),dfs(ch[p][1],mid+1,r);
	}
};
int n,m,w[300010],ret[300010],root[300010],que[300010][2];
treecut<300010,300010> g;
segtree<300010*20> t;
void dfs(int u,int fa,int sgn){
	for(int i=g.head[u];i;i=g.nxt[i]){
		int v=g[i].v; if(v==fa) continue;
		dfs(v,u,sgn),t.merge(root[u],root[v]); 
	}
	ret[u]+=t.query(w[u]+sgn*g.dep[u],root[u]);
//	debug("segtree %d:",u),t.dfs(root[u]),puts("");
}
int main(){
//	#ifdef LOCAL
//	 	freopen("input.in","r",stdin);
//	#endif
	scanf("%d%d",&n,&m);
	for(int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),g.link(u,v);
	g.dfs(1),g.cut(1,1); 
	for(int i=1;i<=n;i++) scanf("%d",&w[i]);
	for(int i=1;i<=m;i++) scanf("%d%d",&que[i][0],&que[i][1]);
	memset(root,t.tot=0,sizeof root);
	for(int i=1;i<=m;i++){
		int u=que[i][0],v=que[i][1],T=g.dist(u,v);
		t.modify(T-g.dep[v],1,root[v]),t.modify(T-g.dep[v],-1,root[g.lca(u,v)]);
	}
	dfs(1,0,-1);
	memset(root,t.tot=0,sizeof root);
	for(int i=1;i<=m;i++){
		int u=que[i][0],v=que[i][1];
		t.modify(g.dep[u],1,root[u]),t.modify(g.dep[u],-1,root[g.fa[g.lca(u,v)]]);
	}
	dfs(1,0,1);
	for(int i=1;i<=n;i++) printf("%d%c",ret[i]," \n"[i==n]);
	return 0;
}
posted @ 2022-11-11 22:56  caijianhong  阅读(21)  评论(0编辑  收藏  举报