zoukankan      html  css  js  c++  java
  • CF494D Birthday

    CF494D Birthday

    题意

    一个1为根的带边权有根树,每次询问给定两个点 (u,v)(sum_{xin S(v)} d(u,x)^2-sum_{x otin S(v)}d(u,x)^2) 其中 (d(u,v)) 表示 (u,v) 简单路径长度, (S(u)) 表示 (u) 的子树内点的集合。

    思路

    考虑 (u)(v) 内和在 (v) 外的情况,发现可以通过每个点维护四个值——子树内所有点到其距离的和、子树内所有点到其距离的平方的和、所有点到其的距离和、所有点到其距离的平方和——来回答询问。

    具体转移细节请自推,这里总结一个重点:

    (v)(u) 两点,(f2[v])(v) 子树内距离平方的和,(f[v])(v) 子树内距离的和,(siz[v])(v) 子树大小,(w) 为两点间距离,则 (v) 子树内所有点到 (u) 的距离的平方和为

    [f2[v]+2*f[v]*w+siz[v]*w^2 ]

    深度优先搜索两边换根处理出以上信息后分类讨论即可。

    代码

    请注意取模。

    代码中的一些取模可以被优化。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cctype>
    #include<algorithm>
    #include<cmath>
    #define int long long
    using namespace std;
    inline int read(){
    	int w=0,x=0;char c=getchar();
    	while(!isdigit(c))w|=c=='-',c=getchar();
    	while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
    	return w?-x:x;
    }
    namespace star
    {
    	const int maxn=1e5+10,mod=1e9+7;
    	int n;
    	int ecnt,head[maxn],to[maxn<<1],nxt[maxn<<1],v[maxn<<1];
    	inline void addedge(int a,int b,int c){
    		to[++ecnt]=b,nxt[ecnt]=head[a],head[a]=ecnt,v[ecnt]=c;
    		to[++ecnt]=a,nxt[ecnt]=head[b],head[b]=ecnt,v[ecnt]=c;
    	}
    	inline int up(int a){return a>=mod?a-mod:a;}
    	inline int dn(int a){return a<0?a+mod:a;}
    	int f[maxn],f2[maxn],g[maxn],g2[maxn],dep[maxn],fa[maxn][21],dis[maxn],siz[maxn];
    	void dfs(int x,int father){
    		fa[x][0]=father;siz[x]=1;
    		dep[x]=dep[father]+1;
    		for(int i=0;i<20;i++) fa[x][i+1]=fa[fa[x][i]][i];
    		for(int i=head[x];i;i=nxt[i]){
    			int u=to[i];
    			if(u==father)continue;
    			dis[u]=dis[x]+v[i];
    			dfs(u,x);
    			siz[x]+=siz[u];
    			f[x]=up(f[x]+up(f[u]+siz[u]*v[i]%mod))%mod;
    			f2[x]=((f2[x]+f2[u])%mod+(siz[u]*v[i]%mod*v[i]%mod+2*v[i]*f[u]%mod)%mod)%mod;
    		}
    	}
    	void dfs2(int x){
    		for(int i=head[x];i;i=nxt[i]){
    			int u=to[i];
    			if(u==fa[x][0])continue;
    			g[u]=(g[x]+(n-2*siz[u]+mod)%mod*v[i]%mod)%mod;
    			g2[u]=((f2[u]+(g2[x]-((f2[u]+2*v[i]%mod*f[u]%mod)%mod+siz[u]*v[i]%mod*v[i]%mod)%mod+mod)%mod)%mod+((g[x]-f[u]+mod-siz[u]*v[i]%mod+mod)%mod*2%mod*v[i]%mod+(n-siz[u])*v[i]%mod*v[i]%mod)%mod)%mod;
    			dfs2(u);
    		}
    	}
    	inline int LCA(int x,int y){
    		if(dep[x]<dep[y])swap(x,y);
    		for(int i=20;~i;i--) if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
    		if(x==y) return x;
    		for(int i=20;~i;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    		return fa[x][0];
    	}
    	inline void work(){
    		n=read();
    		for(int u,v,i=1;i<n;i++) u=read(),v=read(),addedge(u,v,read());
    		dfs(1,0);
    		g[1]=f[1],g2[1]=f2[1];
    		dfs2(1);
    		int Q=read();
    		while(Q--){
    			int y=read(),x=read(),lca=LCA(x,y),d=dis[x]+dis[y]-dis[lca]*2;
    			d%=mod;
    			if(lca==x){
    				printf("%lld
    ",(g2[y]-2*((((g2[x]-f2[x]+mod)%mod+2*d%mod*(g[x]-f[x]+mod)%mod)%mod+d*d%mod*(n-siz[x])%mod)%mod+mod)%mod+mod)%mod);
    			}else{
    				printf("%lld
    ",((((f2[x]%mod+2%mod*d%mod*f[x]%mod)%mod+d%mod*d%mod*siz[x]%mod)%mod*2%mod-g2[y]%mod)%mod+mod)%mod);
    			}
    		}
    	}
    }
    signed main(){
    	star::work();
    	return 0;
    }
    
  • 相关阅读:
    SQLite的使用
    Messenger类的使用
    Binder的使用(跨进程——AIDL,非跨进程)
    Android Studio中如何创建AIDL
    第二章——Parcelable接口的使用(跨进程,Intent传输)
    InetAddress的作用
    第二章——Serializable的使用(跨进程使用和Intent的传递对象)
    SurfaceView绘图机制
    双缓冲机制简介
    内部类代码
  • 原文地址:https://www.cnblogs.com/BrotherHood/p/13889616.html
Copyright © 2011-2022 走看看