zoukankan      html  css  js  c++  java
  • P1600 [NOIP2016 提高组] 天天爱跑步

    我只会这题的 (O(n log n)) 线段树合并做法……感觉树上差分的做法好妙。

    首先把每条路径 (s_i o t_i) 拆成一条向上的路径和一条向下的路径:(s_i o operatorname{LCA}(s_i,t_i) o t_i)。记 (l_i=operatorname{LCA}(s_i,t_i))

    对于向上的路径,可以发现,如果一条路径 (s_i o l_i) 对观察员 (u) 产生了贡献,那么 (uin anc_{s_i})(dep_{s_i}=dep_u+w_u)

    对于向下的路径,推出来 (dep_{s_i}-2 imes dep_{l_i}=w_u-dep_u)

    向上路径的做法和向下路径的做法类似,下面只讨论向上的做法。

    考虑树上差分:将每种数看成一种物品,我们在点 (s_i) 打一个 (+dep_{s_i}) 的标记,在 (fa_{l_i}) 处打一个 (-dep_{s_i}) 的标记。

    我们记录一个全局的数组 (c)(c_i) 表示物品 (i) 的个数。

    我们对树进行 dfs。当 dfs 到点 (u) 时,先记录此时 (c_{w_u-dep_u}) 的值 (cnt),再 dfs 点 (u) 的所有儿子,然后将点 (u) 的所有标记作用到 (c) 数组上,此时点 (u) 作为观察员的答案就会加上 (c_{w_u-dep_u}-cnt)

    为什么这是对的?因为 (c) 数组具有可减性,所以我们可以很方便地去除点 (u) 的祖先的贡献(通过减去 (cnt))。而且我们在更新点 (u) 的答案时,只遍历了点 (u) 的所有子孙。

    代码
    #include <cstdio>
    #include <cstring>
    #include <cctype>
    #include <vector>
    using namespace std;
    #define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti)
    #define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti)
    template<typename T> void Read(T &x){
    	x=0;int _f=1;
    	char ch=getchar();
    	while(!isdigit(ch)) _f=(ch=='-'?-1:_f),ch=getchar();
    	while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();
    	x=x*_f;
    }
    template<typename T,typename... Args> void Read(T &x,Args& ...others){
    	Read(x);Read(others...);
    }
    typedef long long ll;
    const int N=3e5+5;
    int n,m,w[N],s[N],lca[N],t[N];
    vector<int> G[N];
    int dep[N],siz[N],hson[N],fa[N];
    void HLD1(int u){
    	dep[u]=dep[fa[u]]+1,siz[u]=1;
    	for(int v:G[u]){
    		if(v==fa[u]) continue;
    		fa[v]=u;
    		HLD1(v);siz[u]+=siz[v];
    		if(!hson[u]||siz[v]>siz[hson[u]]) hson[u]=v;
    	}
    }
    int top[N];
    void HLD2(int u,int tp){
    	top[u]=tp;
    	if(hson[u]) HLD2(hson[u],top[u]);
    	else return;
    	for(int v:G[u]){
    		if(v==fa[u]||v==hson[u]) continue;
    		HLD2(v,v);
    	}
    }
    int Lca(int u,int v){
    	while(top[u]!=top[v]){
    		if(dep[top[u]]<dep[top[v]]) swap(u,v);
    		u=fa[top[u]];
    	}
    	return dep[u]>dep[v]?v:u;
    }
    int ans[N],c[N<<1];vector<int> chg1[N];
    void Dfs1(int u){
    	int cnt=c[w[u]+dep[u]];
    	for(int v:G[u]){
    		if(v==fa[u]) continue;
    		Dfs1(v);
    	}
    	for(int chg:chg1[u]){
    		if(chg>0) ++c[chg];
    		else --c[-chg];
    	}
    	ans[u]+=c[w[u]+dep[u]]-cnt;
    }
    vector<int> chg2[N];
    void Dfs2(int u){
    	int cnt=c[w[u]-dep[u]+n];
    	for(int v:G[u]){
    		if(v==fa[u]) continue;
    		Dfs2(v);
    	}
    	for(int chg:chg2[u]){
    		if(chg>0) ++c[chg];
    		else --c[-chg];
    	}
    	ans[u]+=c[w[u]-dep[u]+n]-cnt;
    }
    int main(){
    	Read(n,m);
    	int u,v;
    	For(i,1,n-1){
    		Read(u,v);
    		G[u].push_back(v),G[v].push_back(u);
    	}
    	HLD1(1);HLD2(1,1);
    	For(i,1,n) Read(w[i]);
    	For(i,1,m){
    		Read(s[i],t[i]);lca[i]=Lca(s[i],t[i]);
    		chg1[s[i]].push_back(dep[s[i]]);
    		if(fa[lca[i]]) chg1[fa[lca[i]]].push_back(-dep[s[i]]);
    		chg2[t[i]].push_back(dep[s[i]]-2*dep[lca[i]]+n);
    		chg2[lca[i]].push_back(-dep[s[i]]+2*dep[lca[i]]-n);
    	}
    	Dfs1(1);
    	memset(c,0,sizeof c);
    	Dfs2(1);
    	For(i,1,n) printf("%d ",ans[i]);
    	return 0;
    }
    
    Written by Alan_Zhao
  • 相关阅读:
    英语影视台词---三、Cinema Paradiso
    英语影视台词---二、Inception
    智课雅思短语---二、exert positive/ negative effects on…
    MVC 编程模型及其变种
    四:redis的sets类型
    HDU 2048 号码塔(DP)
    删除句子UITableView额外的底线和切割线
    css+html菜单适应性学习的宽度
    删RAC中间ASM和LISTENER 资源的正确方法
    联合县城市,采用ajax,而使用ul模拟select下拉
  • 原文地址:https://www.cnblogs.com/alan-zhao-2007/p/p1600-sol.html
Copyright © 2011-2022 走看看