图论 contest 真保龄记

滨江怎么没有ubuntu啊差评

空调怎么效果这么差啊差评

怎么连饮水机都没有啊差评

凳子怎么没有靠背啊差评

由于某些原因延迟10min

点开题心里一惊:这不比xgzc还毒瘤??!

啊T4点分治模板啊感谢

然后5min敲完后看一眼:这……所有子区间求所有子区间的最大值的和?这TM能做啊

然后看完其他题感觉吃枣药丸

T1很像同余最短路,然而不会

T2暴力都不会

T3瞬间想到Boruvka然而不会写

然后摆烂1h

后来lhl说只切了T2

然后浪子回头,看T2去

手玩了一会样例发现奇数是1,偶数就是把直径抠出来搞搞

然后写+调约20min

然后就zbl,滚粗去写暴力

结果最后就T1的暴力拿了20

然后T2显然假了

于是 20+0+0+0=0

嘿嘿嘿没有爆零嘿嘿嘿

题解?题解有什么好写的……俩原创, 1 joisc, 1 3500*

Update on 7.26:好吧把那个3500*给淦了写一下

对于这类与区间和树都有关的问题,我们考虑分治计算区间 \([l,r]\) 中跨过中点 \(mid\) 的答案,在左边从大到小枚举一个端点 \(i\),如何快速计算所有右端点的贡献呢?

我们发现,随着左端点的缩小,点集是在不断扩大的,那么如果我们能维护所有 \([mid+1,j\in(mid,r]]\) 与当前点集的并,答案就很好算了

这时我们就用到树上圆理论:

首先你要知道一个圆在树上会怎么样

我们定义 \(C(x,r)\) 为树上某点集恰好被完全覆盖在以 \(x\) 为圆心,\(r\) 为半径的圆中以简单维护点集信息

那么 \(2r\) 即这个点集的直径

众所周知,两个圆有三种位置关系:包含,有交,相离

如何合并两个圆?

可以画几张图来理解:

若包含:直接返回大的那个

否则:容易发现新的直径即 \(dist(x_{C1},x_{C2})+r_{C1}+r_{C2}\),而 \(x_{C1}\) 距新圆心的距离为 \(dist(x_{C1},x_{C2})+r_{C1}-r_{C2}\) ,于是我们能在 \(O(1)\) 的时间内解决两圆的合并!

那么左边的圆和右边的圆的并只会有三种情况:左包含右,右包含左,左右合并

而由于右边的点集大小也是单调上升的,所以我们一定能维护三种情况的断点(设为 \(p,q\))然后快速计算答案

讨论三种情况的答案:(令 \(cur\) 为左边的圆)

左包含右:显然是 \(2r_{cur}\times {(p-mid-1)}\)

右包含左:显然是 \(2\sum\limits_{x\ge q}r_{[q,x]}\)

左右合并:即把合并的部分弄个求和

然后会发现所有东西都能搞个前缀和直接维护,于是就做完了

为了纪念而贴上近200行的代码:

点击查看代码
#include<bits/stdc++.h>
#pragma GCC optimize(2,3,"Ofast")
#define int long long
#define inf 1e18
#define N 400005
#define ls k<<1
#define rs k<<1|1
#define mid ((l+r)>>1)
#define mp make_pair
#define pb push_back
#define pii pair<int,int>
#define fi first
#define se second
#define il inline
#define file(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout)
using namespace std;
il int read(){
    int w=0,h=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')h=-h;ch=getchar();}
    while(ch>='0'&&ch<='9'){w=w*10+ch-'0';ch=getchar();}
    return w*h;
}
struct Edge{int next,to;}edge[N<<1];
int n,ans,SumDep[N],SumR[N];
int head[N],num;
void add(int u,int v){
    edge[++num].next=head[u];
    edge[num].to=v;head[u]=num;
}
namespace Fenwick{
    int c1[N],c2[N],res;
  il void add(int c[],int x,int y){for(;x<=n<<1;x+=x&(-x))c[x]+=y;}
    il void add(int x,int y){add(c1,x,y);add(c2,x,x*y);}
    il void add(int l,int r,int y){add(l,y);add(r+1,-y);}
    il int ask(int c[],int x){for(res=0;x;x-=x&(-x))res+=c[x];return res;}
    il int ask(int x){return ask(c1,x)*(x+1)-ask(c2,x);}
    il int ask(int l,int r){return ask(r)-ask(l-1);}
}
using namespace Fenwick;
namespace Tree{
    struct Node{int dep,fa,siz,son,seg,top;}t[N];int dfn;
    void dfs1(int u,int fa){
        t[u].dep=t[fa].dep+1;
        t[u].fa=fa;t[u].siz=1;
        for(int i=head[u];i;i=edge[i].next){
            int v=edge[i].to;
            if(v==fa)continue;
            dfs1(v,u);
            t[u].siz+=t[v].siz;
            if(t[v].siz>t[t[u].son].siz)t[u].son=v;
        }
    }
    void dfs2(int u,int topf){
        t[u].top=topf;
        t[u].seg=++dfn;
        if(t[u].son)dfs2(t[u].son,topf);
        for(int i=head[u];i;i=edge[i].next){
            int v=edge[i].to;
            if(v==t[u].fa||v==t[u].son)continue;
            dfs2(v,v);
        }
    }
    il void Add(int x,int y){
        int fx=t[x].top;
        while(fx)add(t[fx].seg,t[x].seg,y),x=t[fx].fa,fx=t[x].top;
    }
    il int Ask(int x){
        int fx=t[x].top,res=0;
        while(fx)res+=ask(t[fx].seg,t[x].seg),x=t[fx].fa,fx=t[x].top;
        return res;
    }
    void Build(){dfs1(1,0);dfs2(1,1);}
}
namespace Sparse_Table{
	int Min[N<<1][22],lg[N<<1],Dfn[N],Rev[N],dfn;
	void dfs(int u,int fa){
	    dfn++;Rev[dfn]=u;
	    Min[dfn][0]=Dfn[u]=dfn;
		for(int i=head[u];i;i=edge[i].next){
			int v=edge[i].to;
			if(v==fa)continue;
			dfs(v,u);
			Min[++dfn][0]=Dfn[u];
		}
	}
	void Build(){
		dfn=0;dfs(1,0);
		for(int i=2;i<=dfn;i++)lg[i]=lg[i>>1]+1;
		for(int j=1;j<=20;j++)
			for(int i=1;i+(1<<j)-1<=dfn;i++)
				Min[i][j]=min(Min[i][j-1],Min[i+(1<<(j-1))][j-1]);
	}
	il int LCA(int x,int y){
		x=Dfn[x];y=Dfn[y];if(x>y)swap(x,y);int k=lg[y-x+1];
		return Rev[min(Min[x][k],Min[y-(1<<k)+1][k])];
	}
}
using namespace Sparse_Table;
namespace Hitler{
	struct Node{int dep,max,fa[22],son,top;vector<int>up,dn;}t[N];
	void dfs1(int u,int fa){
		t[u].fa[0]=fa;
		t[u].max=t[u].dep=t[fa].dep+1;
		for(int i=1;i<=20;i++)
			t[u].fa[i]=t[t[u].fa[i-1]].fa[i-1];
		for(int i=head[u];i;i=edge[i].next){
			int v=edge[i].to;
			if(v==fa)continue;
			dfs1(v,u);
			if(t[v].max>t[u].max)t[u].max=t[v].max,t[u].son=v;
		}
	}
	void dfs2(int u,int topf){
		t[u].top=topf;
		if(u==topf)
			for(int i=0,up=u,dn=u;i<=t[u].max-t[u].dep;i++)
				t[u].up.pb(up),t[u].dn.pb(dn),up=t[up].fa[0],dn=t[dn].son;
		if(t[u].son)dfs2(t[u].son,topf);
		for(int i=head[u];i;i=edge[i].next){
			int v=edge[i].to;
			if(v==t[u].fa[0]||v==t[u].son)continue;
			dfs2(v,v);
		}
	}
	il int Jump(int u,int k){
		if(k<=0)return u;
		u=t[u].fa[lg[k]];
		k-=((1<<lg[k])+t[u].dep-t[t[u].top].dep);
		u=t[u].top;
		return(k>=0)?t[u].up[k]:t[u].dn[-k];
	}
	void Build(){dfs1(1,0);dfs2(1,1);}
}
namespace Circle{
    struct Cir{
        int id,R;
        bool operator==(const Cir&p)const{return(id==p.id)&&(R==p.R);}
        bool operator!=(const Cir&p)const{return(id!=p.id)||(R!=p.R);}
    }Temp[N];
    il bool CircleIn(Cir x,Cir y){
        int dist=Tree::t[x.id].dep+Tree::t[y.id].dep-(Tree::t[LCA(x.id,y.id)].dep<<1);
        return(y.R+dist<=x.R);
    }
    il int Center(int x,int y,int d){
        int lca=LCA(x,y),dist=Tree::t[x].dep+Tree::t[y].dep-(Tree::t[lca].dep<<1);
//        cout<<x<<' '<<y<<' '<<lca<<' '<<dist<<' '<<d<<endl;
//        cout<<Tree::t[x].dep<<' '<<Tree::t[lca].dep<<endl;
        if(Tree::t[x].dep-Tree::t[lca].dep>=d)return Hitler::Jump(x,d);
        return Hitler::Jump(y,dist-d);
    }
    Cir Merge(Cir x,Cir y){
    	int lca=LCA(x.id,y.id),dist=Tree::t[x.id].dep+Tree::t[y.id].dep-(Tree::t[lca].dep<<1);
    	if(x.R+dist<=y.R)return y;if(y.R+dist<=x.R)return x;
    	return(Cir){Center(x.id,y.id,(dist-x.R+y.R)>>1),(dist+x.R+y.R)>>1};
	}
}
void Divide(int l,int r){
    if(l==r)return;
    int PosIn=mid+1,PosOut=mid+1;
	Circle::Cir cur=(Circle::Cir){mid,0};
    Circle::Temp[mid+1]=(Circle::Cir){mid+1,0};SumDep[mid]=SumR[mid]=0;
    for(int i=mid+2;i<=r;i++)Circle::Temp[i]=Circle::Merge(Circle::Temp[i-1],(Circle::Cir){i,0});
    for(int i=mid+1;i<=r;i++){
    	SumDep[i]=SumDep[i-1]+Tree::t[Circle::Temp[i].id].dep;
    	SumR[i]=SumR[i-1]+Circle::Temp[i].R;
	}
	for(int i=mid;i>=l;i--){
//		puts("HEHE");
		cur=Merge(cur,(Circle::Cir){i,0});
		while(PosIn<=r&&CircleIn(cur,Circle::Temp[PosIn]))Tree::Add(Circle::Temp[PosIn].id,-1),PosIn++;
		while(PosOut<=r&&(cur==Circle::Temp[PosOut]||!Circle::CircleIn(Circle::Temp[PosOut],cur)))
			Tree::Add(Circle::Temp[PosOut].id,1),PosOut++;
		ans+=2*(PosIn-mid-1)*cur.R;
		ans+=2*(SumR[r]-SumR[PosOut-1]);
		ans+=SumDep[PosOut-1]-SumDep[PosIn-1]+SumR[PosOut-1]-SumR[PosIn-1]+(PosOut-PosIn)*(Tree::t[cur.id].dep+cur.R)-2*Tree::Ask(cur.id);
	}
	for(int i=PosIn;i<PosOut;i++)Tree::Add(Circle::Temp[i].id,-1);
    Divide(l,mid);Divide(mid+1,r);
}
signed main(){
    n=read();
    for(int i=2;i<=n;i++){
        int u=read(),v=read();
        ::add(u,i+n);::add(i+n,u);
        ::add(v,i+n);::add(i+n,v);
    }
    Tree::Build();
    Hitler::Build();
    Sparse_Table::Build();
    Divide(1,n);
    printf("%lld\n",ans>>1);
    return 0;
}
posted @ 2022-07-25 20:50  pidan007  阅读(45)  评论(0编辑  收藏  举报