zoukankan      html  css  js  c++  java
  • Loj #6073.「2017 山东一轮集训 Day5」距离

    Loj #6073.「2017 山东一轮集训 Day5」距离

    Description

    给定一棵 (n) 个点的边带权的树,以及一个排列$ p(,有)q $个询问,给定点 (u, v, k),设$ path(u,v) (表示) u$ 到 $v (的路径,)dist(u,v) (表示) u$ 到(v) 的距离,希望你求出
    img

    Input

    第一行一个整数 (type =0/1)表示这个测试点的数据类型。
    第二行两个整数 (n,q)
    接下来$ n−1$ 行,每行三个整数 (ui,vi,ci,)代表树上有一条连接$ ui,vi$ 的权值为$ci $的边。
    接下来一行 (n) 个正整数表示给定的排列 p。
    接下来 (q) 行,每行三个整数 (u′,v′,k′),记lastAns 为上一次询问的答案,假如这是第一次则(lastAns=0),那么这个询问对应的(u,v,k) 满足:
    img

    有一篇写的很不错的博客

    思路还是比较妙啊。

    题目的难点在于求一个点与一个点集的(lca)深度之和。我们可以将点集中的每个点到根的路径上的标记都(+1)。询问点(k)到这个点集的(lca)深度和的时候我们就可以询问该点到根路径上的所有边权与标记的乘积之和。

    由于是询问((a,b))路径上的信息,我们就用主席树维护,询问的时候做差分。对于一个节点(a),它的信息由(fa_a)继承下来,再加上(p_a)的信息就好了。

    考试时不会,写的虚树,常数大到自闭。

    [HNOI2015]开店 的主席树做法也是基于这个原理的。

    代码:

    #include<bits/stdc++.h>
    #define ll long long
    #define N 200005
    
    using namespace std;
    inline ll Get() {ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
    
    int n,m,type;
    int p[N];
    ll pre[N];
    ll ans;
    struct road {
    	int to,next;
    	ll d;
    }s[N<<1];
    int h[N],cnt;
    void add(int i,int j,ll d) {s[++cnt]=(road) {j,h[i],d};h[i]=cnt;}
    
    ll sum_dis[N];
    int size[N],son[N];
    int fa[N],top[N],dep[N];
    ll dis[N];
    
    void dfs(int v) {
    	size[v]=1;
    	for(int i=h[v];i;i=s[i].next) {
    		int to=s[i].to;
    		if(to==fa[v]) continue ;
    		dep[to]=dep[v]+1;
    		dis[to]=dis[v]+s[i].d;
    		fa[to]=v;
    		dfs(to);
    		size[v]+=size[to];
    		if(size[son[v]]<size[to]) son[v]=to;
    	}
    }
    
    int dfn[N],id;
    void dfs2(int v,int tp) {
    	sum_dis[v]=sum_dis[fa[v]]+dis[p[v]];
    	dfn[v]=++id;
    	pre[dfn[v]]=dis[v]-dis[fa[v]];
    	top[v]=tp;
    	if(son[v]) dfs2(son[v],tp);
    	for(int i=h[v];i;i=s[i].next) {
    		int to=s[i].to;
    		if(to==fa[v]||to==son[v]) continue ;
    		dfs2(to,to);
    	}
    }
    
    int lca(int a,int b) {
    	while(top[a]!=top[b]) {
    		if(dep[top[a]]<dep[top[b]]) swap(a,b);
    		a=fa[top[a]];
    	}
    	return dep[a]<dep[b]?a:b;
    }
    
    int tot;
    int rt[N];
    int lx,rx;
    struct tree {
    	int ls,rs;
    	ll tag,sum;
    }tr[N*150];
    
    void Modify(int &v,int old,int lx,int rx,int l,int r,ll f) {
    	if(lx>r||rx<l) return ;
    	v=++tot;
    	tr[v]=tr[old];
    	if(l<=lx&&rx<=r) {
    		tr[v].tag+=f;
    		return ;
    	}
    	int L=max(lx,l),R=min(rx,r);
    	tr[v].sum+=f*(pre[R]-pre[L-1]);
    	int mid=lx+rx>>1;
    	Modify(tr[v].ls,tr[old].ls,lx,mid,l,r,f);
    	Modify(tr[v].rs,tr[old].rs,mid+1,rx,l,r,f);
    }
    
    void Modify(int v) {
    	int a=p[v];
    	while(top[a]!=top[1]) {
    		Modify(rt[v],rt[v],lx,rx,dfn[top[a]],dfn[a],1);
    		a=fa[top[a]];
    	}
    	Modify(rt[v],rt[v],lx,rx,dfn[1],dfn[a],1);
    }
    
    ll query(int v,int lx,int rx,int l,int r) {
    	if(!v||lx>r||rx<l) return 0;
    	if(l<=lx&&rx<=r) return tr[v].sum+tr[v].tag*(pre[rx]-pre[lx-1]);
    	ll ans=tr[v].tag*(pre[min(r,rx)]-pre[max(l,lx)-1]);
    	int mid=lx+rx>>1;
    	ans+=query(tr[v].ls,lx,mid,l,r)+query(tr[v].rs,mid+1,rx,l,r);
    	return ans;
    }
    
    ll query(int rt,int a) {
    	ll ans=0;
    	while(top[a]!=top[1]) {
    		ans+=query(rt,lx,rx,dfn[top[a]],dfn[a]);
    		a=fa[top[a]];
    	}
    	ans+=query(rt,lx,rx,dfn[1],dfn[a]);
    	return ans;
    }
    
    void dfs3(int v) {
    	rt[v]=rt[fa[v]];
    	Modify(v);
    	for(int i=h[v];i;i=s[i].next) {
    		int to=s[i].to;
    		if(to==fa[v]) continue ;
    		dfs3(to);
    	}
    }
    
    int main() {
    	type=Get();
    	n=Get(),m=Get();
    	int a,b,d;
    	for(int i=1;i<n;i++) {
    		a=Get(),b=Get(),d=Get();
    		add(a,b,d),add(b,a,d);
    	}
    	for(int i=1;i<=n;i++) p[i]=Get();
    	lx=1,rx=n;
    	dfs(1);
    	dfs2(1,1);
    	for(int i=1;i<=n;i++) pre[i]+=pre[i-1];
    	dfs3(1);
    	
    	while(m--) {
    		ll x=Get()^(ans*type),y=Get()^(ans*type),k=Get()^(ans*type);
    		int f=lca(x,y);
    		ans=sum_dis[x]+sum_dis[y]-sum_dis[f]-sum_dis[fa[f]];
    		ans+=(dep[x]+dep[y]-2*dep[f]+1)*dis[k];
    		ans-=2*(query(rt[x],k)+query(rt[y],k)-query(rt[f],k)-query(rt[fa[f]],k));
    		cout<<ans<<"
    ";
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    【剑指offer】把字符串转换成整数
    【剑指offer】不用加减乘除做加法
    【剑指offer】求1+2+3+...+n
    【剑指offer】孩子们的游戏(圆圈中最后剩下的数)
    【剑指offer】扑克牌顺子
    【剑指offer】翻转单词顺序列
    【剑指offer】左旋转字符串
    【剑指offer】和为S的两个数字
    【剑指offer】和为S的连续正数序列
    2019.1.10 Mac安装Nginx服务器
  • 原文地址:https://www.cnblogs.com/hchhch233/p/10503205.html
Copyright © 2011-2022 走看看