• CF1458F Range Diameter Sum


    一、题目

    点此看题

    二、解法

    考虑 (f(l,r)) 的实际意义就是保留 ([l,r]) 中的点后树的直径。直径的合并是一个常见结论,但是还不足以解决这道题,这里我们要引入树上圆理论,可以去看看 cmd 的博客(我不想复读一遍)

    考虑移动右端点,维护每个左端点对应的答案,虽然可能有单调性但并不好算出直径。其实这类涉及区间最值求和的问题可以考虑猫树分治,每次只处理过中点的 (f(l,r)),这时候我们移动左指针右指针就会有单调性:第一段是由左边决定的、第二段是共同决定的、第三段是由右边决定的(...)

    我们把这个东西套上邻域理论,我们求出 ([l...mid]) 的圆记为 (A_l),求出 ((mid...r]) 的圆记为 (B_r)

    • 如果 (A_l) 包含 (B_r),那么答案就是 (A_l) 的直径。
    • 如果 (B_r) 包含 (A_l),那么答案就是 (B_r) 的直径。
    • 否则答案是 (A_l) 的半径 (+) (B_r) 的半径 (+) 两个圆的圆心距。

    所以拿两个指针维护即可,最难算的是圆心距,可以拆成深度表达式,那么只需要计算 ( t lca) 的深度和即可,发现这个就是套路的 LCA,写个 ( t bit) 维护树剖即可,时间复杂度 (O(nlog^3 n))

    好难写啊,我调了一个晚上

    #include <cstdio>
    #include <cassert>
    #include <iostream>
    #include <algorithm>
    #include <vector>
    using namespace std;
    const int M = 200005;
    #define int long long
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,Ind,siz[M],sd[M],sr[M],fa[M][20];
    int ans,b1[M],b2[M],top[M],num[M],son[M],dep[M];
    vector<int> g[M];
    //tree dividing
    void dfs1(int u,int p)
    {
    	dep[u]=dep[p]+1;
    	siz[u]=1;fa[u][0]=p;
    	for(int i=1;i<20;i++)
    		fa[u][i]=fa[fa[u][i-1]][i-1];
    	for(auto v:g[u])
    	{
    		if(v==p) continue;
    		dfs1(v,u);
    		siz[u]+=siz[v];
    		if(siz[v]>siz[son[u]]) son[u]=v;
    	}
    }
    void dfs2(int u,int tp)
    {
    	top[u]=tp;num[u]=++Ind;
    	if(son[u]) dfs2(son[u],tp);
    	for(auto v:g[u])
    		if(v^fa[u][0] && v^son[u])
    			dfs2(v,v);
    }
    //bit-array
    int lowbit(int x)
    {
    	return x&(-x);
    }
    void upd(int x,int f)
    {
    	for(int i=x;i<=m;i+=lowbit(i))
    		b1[i]+=f,b2[i]+=(x-1)*f;
    }
    int ask(int x)
    {
    	int r=0;
    	for(int i=x;i>=1;i-=lowbit(i))
    		r+=b1[i]*x,r-=b2[i];
    	return r;
    }
    void ins(int u,int f)
    {
    	while(u)
    	{
    		upd(num[u]+1,-f);
    		upd(num[top[u]],f);
    		u=fa[top[u]][0];
    	}
    }
    int qry(int u)
    {
    	int res=0;
    	while(u)
    	{
    		res+=ask(num[u]);
    		res-=ask(num[top[u]]-1);
    		u=fa[top[u]][0];
    	}
    	return res;
    }
    //find lca and get dis
    int lca(int u,int v)
    {
    	if(dep[u]<dep[v]) swap(u,v);
    	for(int i=19;i>=0;i--)
    		if(dep[fa[u][i]]>=dep[v])
    			u=fa[u][i];
    	if(u==v) return u;
    	for(int i=19;i>=0;i--)
    		if(fa[u][i]^fa[v][i])
    			u=fa[u][i],v=fa[v][i];
    	return fa[u][0];
    }
    int dis(int u,int v)
    {
    	return dep[u]+dep[v]-2*dep[lca(u,v)];
    }
    int jump(int u,int x)
    {
    	for(int i=19;i>=0;i--)
    		if(x&(1<<i)) u=fa[u][i];
    	return u;
    }
    //tree circle structure
    struct node {int x,r;}s[M],q[M];
    bool cmp(node a,node b)
    {
    	return a.r<b.r;
    }
    int get(int u,int v,int x)
    {
    	int p=lca(u,v);
    	if(dep[u]-dep[p]>=x) return jump(u,x);
    	return jump(v,dep[v]-dep[p]-(x-dep[u]+dep[p]));
    }
    node merge(node a,int b)
    {
    	int d=dis(a.x,b);
    	if(a.r>=d) return a;//within
    	return node{get(a.x,b,(d-a.r)/2),(d+a.r)/2};
    }
    int in(node a,node b)//does b contains a yes->1
    {
    	return a.r+dis(a.x,b.x)<=b.r;
    }
    void cdq(int l,int r)
    {
    	if(l==r) return ;
    	int mid=(l+r)>>1;
    	cdq(l,mid);cdq(mid+1,r);
    	s[mid]=node{mid,0};sr[mid]=sd[mid]=0;
    	s[mid+1]=node{mid+1,0};sr[mid+1]=0;
    	//initialize for prefix & suffix
    	for(int i=mid-1;i>=l;i--)
    		s[i]=merge(s[i+1],i);
    	for(int i=mid+2;i<=r;i++)
    	{
    		s[i]=merge(s[i-1],i);
    		sr[i]=sr[i-1]+s[i].r;
    	}
    	for(int i=mid+1;i<=r;i++)
    		sd[i]=sd[i-1]+dep[s[i].x];
    	//cal l<=mid<=r
    	int p1=mid,p2=mid,k=0;
    	for(int i=mid;i>=l;i--)
    	{//(mid,p1] left , (p1,p2] both , (p2,r] right
    		while(p1<r && in(s[p1+1],s[i])) p1++;
    		while(p2<r && !in(s[i],s[p2+1])) p2++;
    		p2=max(p2,p1);
    	//	assert(p1<=p2);
    		ans+=2*(p1-mid)*s[i].r+2*(sr[r]-sr[p2])
    			+(p2-p1)*dep[s[i].x]+(sd[p2]-sd[p1])
    			+(p2-p1)*s[i].r+(sr[p2]-sr[p1]);
    		if(p1<p2)
    		{
    			if(p1>mid) q[++k]=node{-s[i].x,p1};
    			q[++k]=node{s[i].x,p2};
    		}
    	}
    	sort(q+1,q+1+k,cmp);
    	for(int i=mid+1,j=1;i<=r;i++)
    	{
    		ins(s[i].x,1);
    		while(j<=k && q[j].r==i)
    		{
    			int x=q[j].x;
    			if(x<0) ans+=2*qry(-x);
    			else ans-=2*qry(x);
    			j++;
    		}
    	}
    	for(int i=mid+1;i<=r;i++) ins(s[i].x,-1);
    }
    signed main()
    {
    	n=read();m=2*n-1;
    	for(int i=1;i<n;i++)
    	{
    		int u=read(),v=read();
    		g[i+n].push_back(v);
    		g[v].push_back(i+n);
    		g[u].push_back(i+n);
    		g[i+n].push_back(u);
    	}
    	dfs1(1,0);
    	dfs2(1,1);
    	cdq(1,n);
    	printf("%lld
    ",ans/2);
    	//assert(ans%2==0);
    }
    
  • 相关阅读:
    返回顶部
    C# 对文本文件的几种读写方法
    cocos2dx 锁定30帧设置
    AndroidManifest.xml 屏幕上下反转
    粒子系统主
    CCParticleSystem粒子系统
    精灵的优化
    cocos2dx 菜单按钮回调方法传参 tag传参
    cocos2dx跨平台使用自定义字体
    ios7 Cocos2dx 隐藏状态栏设置
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15463528.html
Copyright © 2020-2023  润新知