zoukankan      html  css  js  c++  java
  • CF1458F Range Diameter Sum

    一棵树,定义(diam(l,r))表示区间([l,r])中的点的直径。求(sum_{l<r} diam(l,r))

    (nle 10^5)


    题解有详细证明:https://codeforces.com/blog/entry/85750

    先将所有的边拆成两条,中间插个虚点。

    定义(C(v,r))表示距离点(v)(r)以内的点的集合(可以形象地看成一个圆)。定义(Cover(S))表示一个(C(v,r)),满足(Ssubseteq C(v,r)),且(r)最小。显然(r)为直径的一半,(v)为直径中点。

    接下来有结论:

    对于两个非空点集(S,T),讨论(cover(S),cover(T),cover(Sigcup T))的关系:

    1. 如果(cover(S)subseteq cover(T)),则(cover(Sigcup T)=cover(T))。反之同理。
    2. 否则,设(cover(S)=C(v_S,r_S),cover(T)=C(v_T,r_T),cover(Sigcup T)=(V,R)),则(R=frac 12({r_S+r_T+dis(v_S,v_T)}))(V)(v_S o v_T)方向上距离(R-r_s)的点(即新直径连接两个(v_S,v_T))。

    (C(v_1,r_1)subseteq C(v_2,r_2))的判定:(dis(v_1,v_2)le r_2-r_1)(没错和几何上的圆的内含一样)

    知道了这个之后都是套路了。

    分治。设(c_l(i))表示(cover({i,i+1,dots,mid}))(c_r(j))同理。设(iin [l,mid]),从大往小枚举(i)。对于所有(jin [mid+1,r]),可以分成三段:

    1. (c_r(j)subseteq c_l(i))
    2. (c_r(j))(c_l(i))真相交。
    3. (c_l(i)subseteq c_r(j))

    主要问题是处理第二段的(sum_jdis(c_l(i)_v,c_r(j)_v))。考虑(i)左移的时候,第二段相当于一个从右往左的滑动窗口。维护个数据结构支持:加点、删点、求任意一个点(v)到所有之前加的点的距离和。点分树即可。

    时间复杂度(O(nlg^2 n))


    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cassert>
    #define N 200005
    #define ll long long
    int n;
    struct EDGE{
    	int to;
    	EDGE *las;
    } e[N*2];
    int ne;
    EDGE *last[N];
    void link(int u,int v){
    	e[ne]={v,last[u]};
    	last[u]=e+ne++;
    }
    namespace Tree{
    	int fa[N],dep[N],siz[N],hs[N],top[N],ls[N],re[N],cnt;
    	void dfs1(int x){
    		siz[x]=1;
    		for (EDGE *ei=last[x];ei;ei=ei->las)
    			if (ei->to!=fa[x]){
    				fa[ei->to]=x;
    				dep[ei->to]=dep[x]+1;
    				dfs1(ei->to);
    				siz[x]+=siz[ei->to];
    				if (siz[ei->to]>siz[hs[x]])
    					hs[x]=ei->to;
    			}
    	}
    	void dfs2(int x,int t){
    		top[x]=t;
    		ls[++cnt]=x;
    		re[x]=cnt;
    		if (hs[x]){
    			dfs2(hs[x],t);
    			for (EDGE *ei=last[x];ei;ei=ei->las)
    				if (ei->to!=fa[x] && ei->to!=hs[x])
    					dfs2(ei->to,ei->to);
    		}
    	}
    	int LCA(int u,int v){
    		while (top[u]!=top[v])
    			if (dep[top[u]]>dep[top[v]])
    				u=fa[top[u]];
    			else
    				v=fa[top[v]];
    		return dep[u]<dep[v]?u:v;
    	}
    	int dis(int u,int v){
    		return dep[u]+dep[v]-2*dep[LCA(u,v)];
    	}
    	int kth(int x,int k){
    		assert(k<=dep[x]);
    		while (dep[x]-dep[top[x]]<k){
    			k-=dep[x]-dep[top[x]]+1;
    			x=fa[top[x]];
    		}
    		return ls[re[x]-k];
    	}
    	int find(int u,int v,int k){
    		int lca=LCA(u,v);
    		if (k<=dep[u]-dep[lca])
    			return kth(u,k);
    		return kth(v,dep[u]+dep[v]-2*dep[lca]-k);		
    	}
    	void work(){
    		dfs1(1);
    		dfs2(1,1);
    	}
    }
    namespace CDT{
    	int vis[N];
    	int sz[N],all;
    	int g[N][20],b[N][20];
    	int bs[N][20];
    	ll s[N],c[N],ss[N][20];
    	int getsz(int x,int fa){
    		sz[x]=1;
    		for (EDGE *ei=last[x];ei;ei=ei->las)
    			if (ei->to!=fa && !vis[ei->to]){
    				getsz(ei->to,x);
    				sz[x]+=sz[ei->to];
    			}
    	}
    	int getG(int x,int fa){
    		bool is=(all-sz[x]<=all>>1);
    		for (EDGE *ei=last[x];ei;ei=ei->las)
    			if (ei->to!=fa && !vis[ei->to]){
    				int t=getG(ei->to,x);
    				if (t) return t;
    				is&=(sz[ei->to]<=all>>1);
    			}
    		return is?x:0;
    	}
    	void init(int x,int fa,int d){
    		for (EDGE *ei=last[x];ei;ei=ei->las)
    			if (ei->to!=fa && !vis[ei->to]){
    				g[ei->to][d]=g[x][d]+1;
    				b[ei->to][d]=b[x][d];
    				bs[ei->to][d]=(x==b[x][d]?ei->to:bs[x][d]);
    				init(ei->to,x,d);
    			}
    	}
    	void build(int x,int d){
    		getsz(x,0),all=sz[x],x=getG(x,0);
    		g[x][d]=0,b[x][d]=x,bs[x][d]=x,init(x,0,d);
    		vis[x]=1;
    		for (EDGE *ei=last[x];ei;ei=ei->las)
    			if (!vis[ei->to])
    				build(ei->to,d+1);
    	}
    	void add(int x,int C){
    		for (int i=0;b[x][i];++i){
    			c[b[x][i]]+=C;
    			s[b[x][i]]+=g[x][i]*C;
    			ss[bs[x][i]][i]+=g[x][i]*C;
    		}
    	}
    	ll query(int x){
    		ll res=0;
    		for (int i=0;b[x][i];++i){
    			res+=c[b[x][i]]*g[x][i]+s[b[x][i]]-ss[bs[x][i]][i];
    			if (i)
    				res-=c[b[x][i]]*g[x][i-1];
    		}
    		return res;
    	}
    }
    struct cir{
    	int v,r;
    };
    bool sub(cir s,cir t){
    	return s.r<=t.r && Tree::dis(s.v,t.v)<=t.r-s.r;
    }
    cir merge(cir s,cir t){
    	cir c;
    	int d=Tree::dis(s.v,t.v);
    	if (d<=t.r-s.r) return t;
    	if (d<=s.r-t.r) return s;
    	c.r=(s.r+t.r+d)/2;
    	c.v=Tree::find(s.v,t.v,c.r-s.r);
    	return c;
    }
    cir c[N];
    ll s[N];
    ll ans;
    void divide(int l,int r){
    	if (l==r)
    		return;
    	int mid=l+r>>1;
    	c[mid]={mid,0};
    	c[mid+1]={mid+1,0};
    	for (int i=mid-1;i>=l;--i)
    		c[i]={i,0},c[i]=merge(c[i],c[i+1]);
    	s[mid]=s[mid+1]=0;
    	for (int i=mid+2;i<=r;++i){
    		c[i]={i,0},c[i]=merge(c[i],c[i-1]);
    		s[i]=s[i-1]+c[i].r;
    	}
    	int t1=mid+1,t2=mid+1;
    	for (int i=mid;i>=l;--i){
    		//[mid+1,t1-1] [t1,t2-1] [t2,r]
    		while (t2<=r && !sub(c[i],c[t2])){
    			CDT::add(c[t2].v,1);
    			t2++;
    		}
    		while (t1<=r && t1<t2 && sub(c[t1],c[i])){
    			CDT::add(c[t1].v,-1);
    			t1++;
    		}
    		ans+=(ll)(t1-1-mid)*c[i].r*2+(s[r]-s[t2-1])*2+(t1<t2?((s[t2-1]-s[t1-1])+(ll)c[i].r*(t2-t1)+CDT::query(c[i].v)):0);
    	}
    	for (;t1<t2;++t1)
    		CDT::add(c[t1].v,-1);
    	divide(l,mid);
    	divide(mid+1,r);
    }
    int main(){
    	freopen("in.txt","r",stdin);
    	scanf("%d",&n);
    	for (int i=1;i<n;++i){
    		int u,v;
    		scanf("%d%d",&u,&v);
    		link(u,n+i),link(n+i,u);
    		link(v,n+i),link(n+i,v);
    	}
    	Tree::work();
    	CDT::build(1,0);
    	divide(1,n);
    	printf("%lld
    ",ans/2);
    	return 0;
    }
    
  • 相关阅读:
    HDU 5363 Key Set(快速幂取模)
    HDU 5339 Untitled(暴搜)
    POJ 2406 Power Strings
    Dedecms备份还原网站有效方法
    DEDECMS文章列表每隔8行文章添加分隔虚线
    DEDECMS突破TAG和关键字长度的限制
    为织梦dedecms制作全文RSS订阅源
    DedeCms中Channel用typeid无效
    织梦CMS/Dedecms添加自定义函数
    sql批量换dedecms文章来源和作者
  • 原文地址:https://www.cnblogs.com/jz-597/p/14165423.html
Copyright © 2011-2022 走看看