图论 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;
}